@meshagent/meshagent-react 0.38.2 → 0.38.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +78 -41
  3. package/dist/cjs/client-toolkits.d.ts +2 -2
  4. package/dist/cjs/client-toolkits.js +143 -10
  5. package/dist/cjs/document-connection-scope.d.ts +7 -12
  6. package/dist/cjs/document-connection-scope.js +88 -82
  7. package/dist/cjs/index.d.ts +3 -2
  8. package/dist/cjs/index.js +3 -2
  9. package/dist/cjs/livekit-client.d.ts +23 -0
  10. package/dist/cjs/livekit-client.js +66 -0
  11. package/dist/cjs/livekit-protocol.d.ts +21 -0
  12. package/dist/cjs/livekit-protocol.js +97 -0
  13. package/dist/cjs/room-connection-scope.d.ts +16 -16
  14. package/dist/cjs/room-connection-scope.js +207 -90
  15. package/dist/cjs/room-participants.d.ts +2 -0
  16. package/dist/cjs/room-participants.js +46 -0
  17. package/dist/esm/client-toolkits.d.ts +2 -2
  18. package/dist/esm/client-toolkits.js +145 -12
  19. package/dist/esm/document-connection-scope.d.ts +7 -12
  20. package/dist/esm/document-connection-scope.js +88 -81
  21. package/dist/esm/index.d.ts +3 -2
  22. package/dist/esm/index.js +3 -2
  23. package/dist/esm/livekit-client.d.ts +23 -0
  24. package/dist/esm/livekit-client.js +61 -0
  25. package/dist/esm/livekit-protocol.d.ts +21 -0
  26. package/dist/esm/livekit-protocol.js +60 -0
  27. package/dist/esm/room-connection-scope.d.ts +16 -16
  28. package/dist/esm/room-connection-scope.js +205 -89
  29. package/dist/esm/room-participants.d.ts +2 -0
  30. package/dist/esm/room-participants.js +43 -0
  31. package/package.json +2 -2
  32. package/dist/cjs/chat.d.ts +0 -33
  33. package/dist/cjs/chat.js +0 -207
  34. package/dist/cjs/file-upload.d.ts +0 -43
  35. package/dist/cjs/file-upload.js +0 -168
  36. package/dist/esm/chat.d.ts +0 -33
  37. package/dist/esm/chat.js +0 -201
  38. package/dist/esm/file-upload.d.ts +0 -43
  39. package/dist/esm/file-upload.js +0 -163
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LivekitClient = exports.LivekitConnectionInfo = void 0;
4
+ const meshagent_1 = require("@meshagent/meshagent");
5
+ class LivekitConnectionInfo {
6
+ constructor({ url, token }) {
7
+ Object.defineProperty(this, "url", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: void 0
12
+ });
13
+ Object.defineProperty(this, "token", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: void 0
18
+ });
19
+ this.url = url;
20
+ this.token = token;
21
+ }
22
+ }
23
+ exports.LivekitConnectionInfo = LivekitConnectionInfo;
24
+ class LivekitClient {
25
+ constructor({ room }) {
26
+ Object.defineProperty(this, "room", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: void 0
31
+ });
32
+ this.room = room;
33
+ }
34
+ async getConnectionInfo({ breakoutRoom, } = {}) {
35
+ const response = await this.room.invoke({
36
+ toolkit: 'livekit',
37
+ tool: 'connect',
38
+ input: { breakout_room: breakoutRoom ?? null },
39
+ });
40
+ if (!(response instanceof meshagent_1.JsonContent)) {
41
+ throw new meshagent_1.RoomServerException('unexpected return type from livekit.connect');
42
+ }
43
+ const responseJson = response.json;
44
+ if (typeof responseJson !== 'object'
45
+ || responseJson == null
46
+ || Array.isArray(responseJson)) {
47
+ throw new meshagent_1.RoomServerException('unexpected return type from livekit.connect');
48
+ }
49
+ const token = responseJson.token;
50
+ const url = responseJson.url;
51
+ if (typeof token !== 'string' || typeof url !== 'string') {
52
+ throw new meshagent_1.RoomServerException('unexpected return type from livekit.connect');
53
+ }
54
+ return new LivekitConnectionInfo({ token, url });
55
+ }
56
+ }
57
+ exports.LivekitClient = LivekitClient;
58
+ if (!Object.getOwnPropertyDescriptor(meshagent_1.RoomClient.prototype, 'livekit')) {
59
+ Object.defineProperty(meshagent_1.RoomClient.prototype, 'livekit', {
60
+ configurable: true,
61
+ enumerable: false,
62
+ get() {
63
+ return new LivekitClient({ room: this });
64
+ },
65
+ });
66
+ }
@@ -0,0 +1,21 @@
1
+ import type { ProtocolChannel } from '@meshagent/meshagent';
2
+ import * as livekit from 'livekit-client';
3
+ export declare class LivekitProtocolChannel implements ProtocolChannel {
4
+ private readonly room;
5
+ private readonly remote;
6
+ private readonly topic;
7
+ private onDataReceived?;
8
+ private readonly handleDataReceivedBound;
9
+ constructor({ room, remote, topic, }: {
10
+ room: livekit.Room;
11
+ remote: livekit.RemoteParticipant;
12
+ topic: string;
13
+ });
14
+ start(onDataReceived: (data: Uint8Array) => void, _params: {
15
+ onDone?: () => void;
16
+ onError?: (error: unknown) => void;
17
+ }): void;
18
+ sendData(data: Uint8Array): Promise<void>;
19
+ dispose(): void;
20
+ private handleDataReceived;
21
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LivekitProtocolChannel = void 0;
37
+ const livekit = __importStar(require("livekit-client"));
38
+ class LivekitProtocolChannel {
39
+ constructor({ room, remote, topic, }) {
40
+ Object.defineProperty(this, "room", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: void 0
45
+ });
46
+ Object.defineProperty(this, "remote", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: void 0
51
+ });
52
+ Object.defineProperty(this, "topic", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: void 0
57
+ });
58
+ Object.defineProperty(this, "onDataReceived", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: void 0
63
+ });
64
+ Object.defineProperty(this, "handleDataReceivedBound", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: void 0
69
+ });
70
+ this.room = room;
71
+ this.remote = remote;
72
+ this.topic = topic;
73
+ this.handleDataReceivedBound = this.handleDataReceived.bind(this);
74
+ }
75
+ start(onDataReceived, _params) {
76
+ this.onDataReceived = onDataReceived;
77
+ this.room.on(livekit.RoomEvent.DataReceived, this.handleDataReceivedBound);
78
+ }
79
+ async sendData(data) {
80
+ await this.room.localParticipant.publishData(data, {
81
+ reliable: true,
82
+ topic: this.topic,
83
+ destinationIdentities: [this.remote.identity],
84
+ });
85
+ }
86
+ dispose() {
87
+ this.room.off(livekit.RoomEvent.DataReceived, this.handleDataReceivedBound);
88
+ this.onDataReceived = undefined;
89
+ }
90
+ handleDataReceived(payload, participant, _kind, topic, _encryptionType) {
91
+ const identityMatches = participant?.identity === this.remote.identity;
92
+ if (identityMatches && topic === this.topic) {
93
+ this.onDataReceived?.(payload);
94
+ }
95
+ }
96
+ }
97
+ exports.LivekitProtocolChannel = LivekitProtocolChannel;
@@ -1,8 +1,5 @@
1
- import { RoomClient } from '@meshagent/meshagent';
2
- export interface RoomConnectionInfo {
3
- url: string;
4
- jwt: string;
5
- }
1
+ import { type OAuthTokenRequest, RoomClient, type SecretRequest } from '@meshagent/meshagent';
2
+ import type { RoomConnectionInfo } from '@meshagent/meshagent';
6
3
  export declare const developmentAuthorization: ({ url, projectId, apiKeyId, participantName, roomName, secret, }: {
7
4
  url: string;
8
5
  projectId: string;
@@ -11,27 +8,30 @@ export declare const developmentAuthorization: ({ url, projectId, apiKeyId, part
11
8
  roomName: string;
12
9
  secret: string;
13
10
  }) => (() => Promise<RoomConnectionInfo>);
11
+ export declare const staticAuthorization: ({ projectId, roomName, url, jwt, }: {
12
+ projectId: string;
13
+ roomName: string;
14
+ url: string;
15
+ jwt: string;
16
+ }) => (() => Promise<RoomConnectionInfo>);
14
17
  export interface UseRoomConnectionOptions {
15
- /** Async function that returns `{ url, jwt }` for the room. */
16
- authorization: () => Promise<{
17
- url: string;
18
- jwt: string;
19
- }>;
20
- /** Enable the optional messaging layer (default = `true`). */
18
+ reconnectKey?: string;
19
+ authorization: () => Promise<RoomConnectionInfo>;
21
20
  enableMessaging?: boolean;
21
+ onReady?: (room: RoomClient) => void;
22
+ oauthTokenRequestHandler?: (room: RoomClient, request: OAuthTokenRequest) => Promise<void> | void;
23
+ secretRequestHandler?: (room: RoomClient, request: SecretRequest) => Promise<void> | void;
24
+ roomClientFactory?: (connectionInfo: RoomConnectionInfo) => RoomClient;
22
25
  }
23
- /**
24
- * Shape of the object returned by the hook.
25
- */
26
26
  export interface UseRoomConnectionResult {
27
27
  client: RoomClient | null;
28
- state: 'authorizing' | 'connecting' | 'ready' | 'done';
28
+ state: 'authorizing' | 'connecting' | 'retrying' | 'ready' | 'done';
29
29
  ready: boolean;
30
30
  done: boolean;
31
31
  error: unknown;
32
32
  dispose: () => void;
33
33
  }
34
- export declare function useRoomConnection(props: UseRoomConnectionOptions): UseRoomConnectionResult;
34
+ export declare function useRoomConnection({ reconnectKey, authorization, enableMessaging, onReady, oauthTokenRequestHandler, secretRequestHandler, roomClientFactory, }: UseRoomConnectionOptions): UseRoomConnectionResult;
35
35
  export interface UseRoomIndicatorsResult {
36
36
  typing: boolean;
37
37
  thinking: boolean;
@@ -1,17 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.developmentAuthorization = void 0;
3
+ exports.staticAuthorization = exports.developmentAuthorization = void 0;
4
4
  exports.useRoomConnection = useRoomConnection;
5
5
  exports.useRoomIndicators = useRoomIndicators;
6
6
  const react_1 = require("react");
7
- const subscribe_async_gen_1 = require("./subscribe-async-gen");
8
7
  const meshagent_1 = require("@meshagent/meshagent");
9
- const meshagent_2 = require("@meshagent/meshagent");
8
+ const subscribe_async_gen_1 = require("./subscribe-async-gen");
9
+ const retryBaseDelayMs = 500;
10
+ const retryMaxDelayMs = 30000;
11
+ function getRetryDelayMs(retryCount) {
12
+ return Math.min(retryMaxDelayMs, retryBaseDelayMs * 2 ** retryCount);
13
+ }
14
+ function isRetryableConnectionError(error) {
15
+ return error instanceof meshagent_1.RoomServerException && error.retryable;
16
+ }
10
17
  /* -------------------------------------------------
11
18
  * Authorization helpers
12
19
  * ------------------------------------------------- */
13
20
  const developmentAuthorization = ({ url, projectId, apiKeyId, participantName, roomName, secret, }) => async () => {
14
- const token = new meshagent_2.ParticipantToken({
21
+ const token = new meshagent_1.ParticipantToken({
15
22
  name: participantName,
16
23
  projectId,
17
24
  apiKeyId,
@@ -19,72 +26,171 @@ const developmentAuthorization = ({ url, projectId, apiKeyId, participantName, r
19
26
  token.addRoomGrant(roomName);
20
27
  token.addRoleGrant('user');
21
28
  const jwt = await token.toJwt({ token: secret });
22
- return { url, jwt };
29
+ return {
30
+ jwt,
31
+ projectId,
32
+ roomName,
33
+ roomUrl: url,
34
+ };
23
35
  };
24
36
  exports.developmentAuthorization = developmentAuthorization;
25
- function useRoomConnection(props) {
26
- const { authorization, enableMessaging = true } = props;
37
+ const staticAuthorization = ({ projectId, roomName, url, jwt, }) => async () => ({
38
+ jwt,
39
+ projectId,
40
+ roomName,
41
+ roomUrl: url,
42
+ });
43
+ exports.staticAuthorization = staticAuthorization;
44
+ function useRoomConnection({ reconnectKey, authorization, enableMessaging = true, onReady, oauthTokenRequestHandler, secretRequestHandler, roomClientFactory, }) {
27
45
  const [client, setClient] = (0, react_1.useState)(null);
28
46
  const [ready, setReady] = (0, react_1.useState)(false);
29
47
  const [state, setState] = (0, react_1.useState)('authorizing');
30
48
  const [error, setError] = (0, react_1.useState)(null);
31
- // Keep the latest client in a ref so we can call `dispose` in cleanup.
32
49
  const clientRef = (0, react_1.useRef)(null);
33
- clientRef.current = client;
34
- // Instance method exposed to consumers (rarely needed).
35
- const dispose = () => {
36
- clientRef.current?.dispose();
37
- setState('done');
38
- };
50
+ const cancelConnectionRef = (0, react_1.useRef)(() => { });
39
51
  (0, react_1.useEffect)(() => {
40
52
  let cancelled = false;
41
- const connect = async () => {
42
- try {
43
- // 1️⃣ Get connection credentials
44
- const { url, jwt } = await authorization();
45
- if (cancelled)
53
+ let retryTimeout = null;
54
+ const clearRetryTimeout = () => {
55
+ if (retryTimeout != null) {
56
+ clearTimeout(retryTimeout);
57
+ retryTimeout = null;
58
+ }
59
+ };
60
+ const disposeClient = (room) => {
61
+ if (room == null) {
62
+ return;
63
+ }
64
+ if (clientRef.current === room) {
65
+ clientRef.current = null;
66
+ }
67
+ room.dispose();
68
+ };
69
+ const waitForRetry = (delayMs) => new Promise((resolve) => {
70
+ retryTimeout = setTimeout(() => {
71
+ retryTimeout = null;
72
+ resolve();
73
+ }, delayMs);
74
+ });
75
+ setClient(null);
76
+ setReady(false);
77
+ const cancelConnection = ({ updateState }) => {
78
+ if (cancelled) {
79
+ return;
80
+ }
81
+ cancelled = true;
82
+ clearRetryTimeout();
83
+ const currentClient = clientRef.current;
84
+ clientRef.current = null;
85
+ disposeClient(currentClient);
86
+ if (updateState) {
87
+ setClient(null);
88
+ setReady(false);
89
+ }
90
+ };
91
+ cancelConnectionRef.current = () => cancelConnection({ updateState: true });
92
+ void (async () => {
93
+ let retryCount = 0;
94
+ while (!cancelled) {
95
+ let connectionInfo;
96
+ try {
97
+ setState('authorizing');
98
+ setError(null);
99
+ connectionInfo = await authorization();
100
+ }
101
+ catch (nextError) {
102
+ if (cancelled) {
103
+ return;
104
+ }
105
+ setError(nextError);
106
+ setState('done');
107
+ setReady(false);
46
108
  return;
47
- const room = new meshagent_2.RoomClient({
48
- protocolFactory: meshagent_2.WebSocketClientProtocol.createFactory({ url, token: jwt }),
49
- });
50
- setClient(room);
51
- setState('connecting');
52
- await room.start({
53
- onDone: () => {
54
- if (cancelled)
55
- return;
56
- setState('done');
57
- },
58
- onError: (e) => {
59
- if (cancelled)
60
- return;
61
- setError(e);
62
- setState('done');
63
- },
64
- });
65
- if (enableMessaging) {
66
- room.messaging.enable();
67
109
  }
68
- if (cancelled)
110
+ if (cancelled) {
69
111
  return;
70
- setState('ready');
71
- setReady(true);
72
- }
73
- catch (e) {
74
- if (cancelled)
112
+ }
113
+ let nextClient;
114
+ if (roomClientFactory != null) {
115
+ nextClient = roomClientFactory(connectionInfo);
116
+ }
117
+ else {
118
+ nextClient = new meshagent_1.RoomClient({
119
+ protocolFactory: meshagent_1.WebSocketClientProtocol.createFactory({
120
+ url: connectionInfo.roomUrl,
121
+ token: connectionInfo.jwt,
122
+ }),
123
+ oauthTokenRequestHandler: oauthTokenRequestHandler == null
124
+ ? undefined
125
+ : (request) => oauthTokenRequestHandler(nextClient, request),
126
+ secretRequestHandler: secretRequestHandler == null
127
+ ? undefined
128
+ : (request) => secretRequestHandler(nextClient, request),
129
+ });
130
+ }
131
+ clientRef.current = nextClient;
132
+ setClient(nextClient);
133
+ setReady(false);
134
+ setState('connecting');
135
+ try {
136
+ await nextClient.start({
137
+ onDone: () => {
138
+ if (cancelled || clientRef.current !== nextClient) {
139
+ return;
140
+ }
141
+ setReady(false);
142
+ setState('done');
143
+ },
144
+ onError: (nextError) => {
145
+ if (cancelled || clientRef.current !== nextClient) {
146
+ return;
147
+ }
148
+ setError(nextError);
149
+ setReady(false);
150
+ setState('done');
151
+ },
152
+ });
153
+ if (enableMessaging) {
154
+ nextClient.messaging.enable();
155
+ }
156
+ if (cancelled || clientRef.current !== nextClient) {
157
+ disposeClient(nextClient);
158
+ return;
159
+ }
160
+ retryCount = 0;
161
+ setError(null);
162
+ setReady(true);
163
+ setState('ready');
164
+ onReady?.(nextClient);
75
165
  return;
76
- setError(e);
77
- setState('done');
166
+ }
167
+ catch (nextError) {
168
+ disposeClient(nextClient);
169
+ if (cancelled) {
170
+ return;
171
+ }
172
+ setClient((currentClient) => currentClient === nextClient ? null : currentClient);
173
+ setReady(false);
174
+ setError(nextError);
175
+ if (!isRetryableConnectionError(nextError)) {
176
+ setState('done');
177
+ return;
178
+ }
179
+ setState('retrying');
180
+ await waitForRetry(getRetryDelayMs(retryCount));
181
+ retryCount += 1;
182
+ }
78
183
  }
79
- };
80
- connect();
184
+ })();
81
185
  return () => {
82
- // React unmount or deps change → cancel & dispose
83
- cancelled = true;
84
- dispose();
186
+ cancelConnection({ updateState: false });
187
+ cancelConnectionRef.current = () => { };
85
188
  };
86
- // eslint‑disable‑next‑line react-hooks/exhaustive-deps
87
- }, []); // run once, just like componentDidMount
189
+ }, [reconnectKey, enableMessaging, roomClientFactory]);
190
+ const dispose = () => {
191
+ cancelConnectionRef.current();
192
+ setState('done');
193
+ };
88
194
  return {
89
195
  client,
90
196
  state,
@@ -100,48 +206,59 @@ function useRoomIndicators({ room, path }) {
100
206
  const [typing, setTyping] = (0, react_1.useState)(false);
101
207
  const [thinking, setThinking] = (0, react_1.useState)(false);
102
208
  (0, react_1.useEffect)(() => {
103
- if (!room)
209
+ if (!room) {
104
210
  return;
105
- const s = (0, subscribe_async_gen_1.subscribe)(room.listen(), {
211
+ }
212
+ setTyping(false);
213
+ setThinking(false);
214
+ const subscription = (0, subscribe_async_gen_1.subscribe)(room.listen(), {
106
215
  next: (event) => {
107
- if (event instanceof meshagent_1.RoomMessageEvent) {
108
- const { message } = event;
109
- // Ignore messages from ourselves
110
- if (message.fromParticipantId === room.localParticipant?.id) {
111
- return;
112
- }
113
- // Ignore messages not for this path
114
- if (message.message.path !== path) {
115
- return;
116
- }
117
- if (message.type === "typing") {
118
- // Clear any existing timer for this participant
119
- clearTimeout(typingMap.current[message.fromParticipantId]);
120
- // Set a new timer to remove typing after 1 second
121
- typingMap.current[message.fromParticipantId] = setTimeout(() => {
122
- delete typingMap.current[message.fromParticipantId];
123
- setTyping(Object.keys(typingMap.current).length > 0);
124
- }, 1000);
125
- // Update typing state
216
+ if (!(event instanceof meshagent_1.RoomMessageEvent)) {
217
+ return;
218
+ }
219
+ const { message } = event;
220
+ if (message.fromParticipantId === room.localParticipant?.id) {
221
+ return;
222
+ }
223
+ if (message.message.path !== path) {
224
+ return;
225
+ }
226
+ if (message.type === 'typing') {
227
+ clearTimeout(typingMap.current[message.fromParticipantId]);
228
+ typingMap.current[message.fromParticipantId] = setTimeout(() => {
229
+ delete typingMap.current[message.fromParticipantId];
126
230
  setTyping(Object.keys(typingMap.current).length > 0);
127
- }
128
- else if (message.type === "thinking") {
129
- clearTimeout(thinkingMap.current[message.fromParticipantId]);
130
- if (message.message.thinking) {
131
- thinkingMap.current[message.fromParticipantId] = setTimeout(() => {
132
- delete thinkingMap.current[message.fromParticipantId];
133
- setThinking(Object.keys(thinkingMap.current).length > 0);
134
- }, 5000);
135
- }
136
- else {
137
- delete thinkingMap.current[message.fromParticipantId];
138
- }
231
+ }, 1000);
232
+ setTyping(Object.keys(typingMap.current).length > 0);
233
+ return;
234
+ }
235
+ if (message.type !== 'thinking') {
236
+ return;
237
+ }
238
+ clearTimeout(thinkingMap.current[message.fromParticipantId]);
239
+ if (message.message.thinking) {
240
+ thinkingMap.current[message.fromParticipantId] = setTimeout(() => {
241
+ delete thinkingMap.current[message.fromParticipantId];
139
242
  setThinking(Object.keys(thinkingMap.current).length > 0);
140
- }
243
+ }, 5000);
244
+ }
245
+ else {
246
+ delete thinkingMap.current[message.fromParticipantId];
141
247
  }
248
+ setThinking(Object.keys(thinkingMap.current).length > 0);
142
249
  },
143
250
  });
144
- return () => s.unsubscribe();
145
- }, [room, path]);
251
+ return () => {
252
+ subscription.unsubscribe();
253
+ for (const timeout of Object.values(typingMap.current)) {
254
+ clearTimeout(timeout);
255
+ }
256
+ for (const timeout of Object.values(thinkingMap.current)) {
257
+ clearTimeout(timeout);
258
+ }
259
+ typingMap.current = {};
260
+ thinkingMap.current = {};
261
+ };
262
+ }, [path, room]);
146
263
  return { typing, thinking };
147
264
  }
@@ -0,0 +1,2 @@
1
+ import { RoomClient, RemoteParticipant } from '@meshagent/meshagent';
2
+ export declare function useRoomParticipants(room: RoomClient | null): RemoteParticipant[];
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useRoomParticipants = useRoomParticipants;
4
+ const react_1 = require("react");
5
+ function sameParticipantIds(currentParticipants, nextParticipants) {
6
+ if (currentParticipants.length !== nextParticipants.length) {
7
+ return false;
8
+ }
9
+ const currentParticipantIds = new Set(currentParticipants.map((participant) => participant.id));
10
+ for (const participant of nextParticipants) {
11
+ if (!currentParticipantIds.has(participant.id)) {
12
+ return false;
13
+ }
14
+ }
15
+ return true;
16
+ }
17
+ function useRoomParticipants(room) {
18
+ const [participants, setParticipants] = (0, react_1.useState)([]);
19
+ (0, react_1.useEffect)(() => {
20
+ if (room == null) {
21
+ setParticipants([]);
22
+ return;
23
+ }
24
+ const updateParticipants = () => {
25
+ const nextParticipants = room.messaging.remoteParticipants;
26
+ setParticipants((currentParticipants) => {
27
+ if (sameParticipantIds(currentParticipants, nextParticipants)) {
28
+ return currentParticipants;
29
+ }
30
+ return [...nextParticipants];
31
+ });
32
+ };
33
+ room.messaging.on('participant_added', updateParticipants);
34
+ room.messaging.on('participant_removed', updateParticipants);
35
+ room.messaging.on('messaging_enabled', updateParticipants);
36
+ room.on('disconnected', updateParticipants);
37
+ updateParticipants();
38
+ return () => {
39
+ room.messaging.off('participant_added', updateParticipants);
40
+ room.messaging.off('participant_removed', updateParticipants);
41
+ room.messaging.off('messaging_enabled', updateParticipants);
42
+ room.off('disconnected', updateParticipants);
43
+ };
44
+ }, [room]);
45
+ return participants;
46
+ }
@@ -1,8 +1,8 @@
1
- import { RoomClient, Toolkit } from '@meshagent/meshagent';
1
+ import { RoomClient, Toolkit } from "@meshagent/meshagent";
2
2
  interface ClientToolkitsProps {
3
3
  room: RoomClient;
4
4
  toolkits: Toolkit[];
5
5
  public?: boolean;
6
6
  }
7
- export declare const useClientToolkits: ({ room, toolkits, public: isPublic }: ClientToolkitsProps) => void;
7
+ export declare const useClientToolkits: ({ room, toolkits, public: isPublic, }: ClientToolkitsProps) => void;
8
8
  export {};