@defcomm/meetingsdk 0.4.2 → 0.4.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.
package/dist/index.d.mts CHANGED
@@ -59,6 +59,14 @@ type ChatMessage = {
59
59
  } | null;
60
60
  target?: string | null;
61
61
  };
62
+ type PubSubMessage<T = unknown> = {
63
+ id: string;
64
+ topic: PubSubTopic;
65
+ sender_id: string;
66
+ sender_name?: string | null;
67
+ timestamp: number;
68
+ data: T;
69
+ };
62
70
  type Participant = {
63
71
  id: string;
64
72
  name?: string;
@@ -97,7 +105,11 @@ type MeetingConfig = {
97
105
  videoMuted?: boolean;
98
106
  token?: string;
99
107
  };
100
- type PubSubTopic = "SECURE_CHAT";
108
+ type PubSubTopic = "SECURE_CHAT" | "REACTION";
109
+ type PubSubHandle<T = unknown> = {
110
+ messages: PubSubMessage<T>[];
111
+ publish: (data: T) => void;
112
+ };
101
113
  type RecordingInfo = {
102
114
  recording_id: string;
103
115
  started_at?: string;
@@ -145,6 +157,12 @@ interface VideoSDKCoreOptions {
145
157
  onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
146
158
  }
147
159
  type SFUConnectionState = "idle" | "connecting" | "joining" | "waiting_approval" | "connected" | "reconnecting" | "leaving" | "disconnected";
160
+ type JoinState = {
161
+ status: "ready";
162
+ reason: "admitted" | "open" | "moderator";
163
+ } | {
164
+ status: "approval_required";
165
+ };
148
166
 
149
167
  declare const useLocalParticipant: () => {
150
168
  participant: Participant | null;
@@ -212,6 +230,7 @@ declare class VideoSDKCore {
212
230
  is_private?: boolean;
213
231
  }): Promise<any>;
214
232
  getRoom(roomCode: string): Promise<any>;
233
+ getJoinState(roomCode: string): Promise<JoinState>;
215
234
  deleteRoom(roomCode: string): Promise<any>;
216
235
  joinMeeting(config: Omit<MeetingConfig, "participantId">): Promise<void>;
217
236
  private connect;
@@ -239,15 +258,6 @@ declare class VideoSDKCore {
239
258
  private performDisconnect;
240
259
  }
241
260
 
242
- /**
243
- * DefComm SFU client.
244
- *
245
- * Signaling is authoritative for admission/session state.
246
- * WebRTC peer connections are transport state and are recreated only after a
247
- * successful join/resume. A transient WebSocket close therefore does NOT
248
- * destroy the local MediaStream or immediately declare the participant gone.
249
- */
250
-
251
261
  interface TrackInfo {
252
262
  track_id: string;
253
263
  participant_id: string;
@@ -265,13 +275,10 @@ interface ParticipantInfo {
265
275
  [key: string]: any;
266
276
  }
267
277
 
268
- type PubSubHandle = {
269
- messages: ChatMessage[];
270
- publish: (input: ChatInput) => void;
271
- };
272
278
  type MeetingContextValue = {
273
279
  sdk: VideoSDKCore;
274
280
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
281
+ getJoinState: (roomCode: string) => Promise<JoinState>;
275
282
  leave: () => void;
276
283
  createRoom: (input: {
277
284
  name: string;
@@ -297,7 +304,8 @@ type MeetingContextValue = {
297
304
  presenterId: string | null;
298
305
  activeSpeakerIds: string[];
299
306
  recording: RecordingInfo | null;
300
- usePubSub: (topic: PubSubTopic) => PubSubHandle;
307
+ usePubSub: <T = unknown>(topic: PubSubTopic) => PubSubHandle<T>;
308
+ onPubSubMessage: <T = unknown>(topic: PubSubTopic, cb: (message: PubSubMessage<T>) => void) => () => void;
301
309
  approveJoinRequest: (requestId: string) => void;
302
310
  rejectJoinRequest: (requestId: string) => void;
303
311
  startRecording: () => Promise<void>;
@@ -333,6 +341,7 @@ declare const useMeeting: (handlers?: {
333
341
  onConnectionStateChanged?: (state: SFUConnectionState) => void;
334
342
  }) => {
335
343
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
344
+ getJoinState: (roomCode: string) => Promise<JoinState>;
336
345
  leave: () => void;
337
346
  createRoom: (input: {
338
347
  name: string;
@@ -358,10 +367,8 @@ declare const useMeeting: (handlers?: {
358
367
  presenterId: string | null;
359
368
  activeSpeakerIds: string[];
360
369
  recording: RecordingInfo | null;
361
- usePubSub: (topic: PubSubTopic) => {
362
- messages: ChatMessage[];
363
- publish: (input: ChatInput) => void;
364
- };
370
+ usePubSub: <T = unknown>(topic: PubSubTopic) => PubSubHandle<T>;
371
+ onPubSubMessage: <T = unknown>(topic: PubSubTopic, cb: (message: PubSubMessage<T>) => void) => () => void;
365
372
  approveJoinRequest: (requestId: string) => void;
366
373
  rejectJoinRequest: (requestId: string) => void;
367
374
  startRecording: () => Promise<void>;
@@ -456,4 +463,8 @@ declare class ActiveSpeakerDetector {
456
463
  destroy(): Promise<void>;
457
464
  }
458
465
 
459
- export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
466
+ declare function usePubSub<T = unknown>(topic: PubSubTopic, options?: {
467
+ onMessageReceived?: (message: PubSubMessage<T>) => void;
468
+ }): PubSubHandle<T>;
469
+
470
+ export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, usePubSub, useRemoteMedia };
package/dist/index.d.ts CHANGED
@@ -59,6 +59,14 @@ type ChatMessage = {
59
59
  } | null;
60
60
  target?: string | null;
61
61
  };
62
+ type PubSubMessage<T = unknown> = {
63
+ id: string;
64
+ topic: PubSubTopic;
65
+ sender_id: string;
66
+ sender_name?: string | null;
67
+ timestamp: number;
68
+ data: T;
69
+ };
62
70
  type Participant = {
63
71
  id: string;
64
72
  name?: string;
@@ -97,7 +105,11 @@ type MeetingConfig = {
97
105
  videoMuted?: boolean;
98
106
  token?: string;
99
107
  };
100
- type PubSubTopic = "SECURE_CHAT";
108
+ type PubSubTopic = "SECURE_CHAT" | "REACTION";
109
+ type PubSubHandle<T = unknown> = {
110
+ messages: PubSubMessage<T>[];
111
+ publish: (data: T) => void;
112
+ };
101
113
  type RecordingInfo = {
102
114
  recording_id: string;
103
115
  started_at?: string;
@@ -145,6 +157,12 @@ interface VideoSDKCoreOptions {
145
157
  onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
146
158
  }
147
159
  type SFUConnectionState = "idle" | "connecting" | "joining" | "waiting_approval" | "connected" | "reconnecting" | "leaving" | "disconnected";
160
+ type JoinState = {
161
+ status: "ready";
162
+ reason: "admitted" | "open" | "moderator";
163
+ } | {
164
+ status: "approval_required";
165
+ };
148
166
 
149
167
  declare const useLocalParticipant: () => {
150
168
  participant: Participant | null;
@@ -212,6 +230,7 @@ declare class VideoSDKCore {
212
230
  is_private?: boolean;
213
231
  }): Promise<any>;
214
232
  getRoom(roomCode: string): Promise<any>;
233
+ getJoinState(roomCode: string): Promise<JoinState>;
215
234
  deleteRoom(roomCode: string): Promise<any>;
216
235
  joinMeeting(config: Omit<MeetingConfig, "participantId">): Promise<void>;
217
236
  private connect;
@@ -239,15 +258,6 @@ declare class VideoSDKCore {
239
258
  private performDisconnect;
240
259
  }
241
260
 
242
- /**
243
- * DefComm SFU client.
244
- *
245
- * Signaling is authoritative for admission/session state.
246
- * WebRTC peer connections are transport state and are recreated only after a
247
- * successful join/resume. A transient WebSocket close therefore does NOT
248
- * destroy the local MediaStream or immediately declare the participant gone.
249
- */
250
-
251
261
  interface TrackInfo {
252
262
  track_id: string;
253
263
  participant_id: string;
@@ -265,13 +275,10 @@ interface ParticipantInfo {
265
275
  [key: string]: any;
266
276
  }
267
277
 
268
- type PubSubHandle = {
269
- messages: ChatMessage[];
270
- publish: (input: ChatInput) => void;
271
- };
272
278
  type MeetingContextValue = {
273
279
  sdk: VideoSDKCore;
274
280
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
281
+ getJoinState: (roomCode: string) => Promise<JoinState>;
275
282
  leave: () => void;
276
283
  createRoom: (input: {
277
284
  name: string;
@@ -297,7 +304,8 @@ type MeetingContextValue = {
297
304
  presenterId: string | null;
298
305
  activeSpeakerIds: string[];
299
306
  recording: RecordingInfo | null;
300
- usePubSub: (topic: PubSubTopic) => PubSubHandle;
307
+ usePubSub: <T = unknown>(topic: PubSubTopic) => PubSubHandle<T>;
308
+ onPubSubMessage: <T = unknown>(topic: PubSubTopic, cb: (message: PubSubMessage<T>) => void) => () => void;
301
309
  approveJoinRequest: (requestId: string) => void;
302
310
  rejectJoinRequest: (requestId: string) => void;
303
311
  startRecording: () => Promise<void>;
@@ -333,6 +341,7 @@ declare const useMeeting: (handlers?: {
333
341
  onConnectionStateChanged?: (state: SFUConnectionState) => void;
334
342
  }) => {
335
343
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
344
+ getJoinState: (roomCode: string) => Promise<JoinState>;
336
345
  leave: () => void;
337
346
  createRoom: (input: {
338
347
  name: string;
@@ -358,10 +367,8 @@ declare const useMeeting: (handlers?: {
358
367
  presenterId: string | null;
359
368
  activeSpeakerIds: string[];
360
369
  recording: RecordingInfo | null;
361
- usePubSub: (topic: PubSubTopic) => {
362
- messages: ChatMessage[];
363
- publish: (input: ChatInput) => void;
364
- };
370
+ usePubSub: <T = unknown>(topic: PubSubTopic) => PubSubHandle<T>;
371
+ onPubSubMessage: <T = unknown>(topic: PubSubTopic, cb: (message: PubSubMessage<T>) => void) => () => void;
365
372
  approveJoinRequest: (requestId: string) => void;
366
373
  rejectJoinRequest: (requestId: string) => void;
367
374
  startRecording: () => Promise<void>;
@@ -456,4 +463,8 @@ declare class ActiveSpeakerDetector {
456
463
  destroy(): Promise<void>;
457
464
  }
458
465
 
459
- export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
466
+ declare function usePubSub<T = unknown>(topic: PubSubTopic, options?: {
467
+ onMessageReceived?: (message: PubSubMessage<T>) => void;
468
+ }): PubSubHandle<T>;
469
+
470
+ export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, usePubSub, useRemoteMedia };
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  useMeetingContext: () => useMeetingContext,
30
30
  useMeetingPreview: () => useMeetingPreview,
31
31
  useParticipants: () => useParticipants,
32
+ usePubSub: () => usePubSub,
32
33
  useRemoteMedia: () => useRemoteMedia
33
34
  });
34
35
  module.exports = __toCommonJS(index_exports);
@@ -596,6 +597,11 @@ var SFUClient = class {
596
597
  `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}`
597
598
  );
598
599
  }
600
+ async getJoinState(roomCode) {
601
+ return this.request(
602
+ `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}/join-state`
603
+ );
604
+ }
599
605
  async deleteRoom(roomCode) {
600
606
  return this.request(
601
607
  `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}`,
@@ -2054,6 +2060,14 @@ var VideoSDKCore = class {
2054
2060
  });
2055
2061
  return client.getRoom(roomCode);
2056
2062
  }
2063
+ async getJoinState(roomCode) {
2064
+ const client = this.sfuClient ?? new SFUClient({
2065
+ apiBase: this.apiBase,
2066
+ wsBase: this.wsBase,
2067
+ participantId: this.myId
2068
+ });
2069
+ return client.getJoinState(roomCode);
2070
+ }
2057
2071
  async deleteRoom(roomCode) {
2058
2072
  const client = this.sfuClient ?? new SFUClient({
2059
2073
  apiBase: this.apiBase,
@@ -2812,6 +2826,9 @@ var MeetingProvider = ({
2812
2826
  const recordingStartedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2813
2827
  const recordingStoppedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2814
2828
  const reactionListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2829
+ const pubSubListeners = (0, import_react2.useRef)(
2830
+ /* @__PURE__ */ new Map()
2831
+ );
2815
2832
  const activeSpeakerListeners = (0, import_react2.useRef)(
2816
2833
  /* @__PURE__ */ new Set()
2817
2834
  );
@@ -2847,6 +2864,19 @@ var MeetingProvider = ({
2847
2864
  },
2848
2865
  onReaction: (reaction) => {
2849
2866
  reactionListeners.current.forEach((fn) => fn(reaction));
2867
+ const listeners = pubSubListeners.current.get("REACTION");
2868
+ if (!listeners) return;
2869
+ const message = {
2870
+ id: reaction.id,
2871
+ topic: "REACTION",
2872
+ sender_id: reaction.participant_id,
2873
+ sender_name: reaction.sender_name,
2874
+ timestamp: typeof reaction.timestamp === "number" ? reaction.timestamp : Date.parse(reaction.timestamp),
2875
+ data: {
2876
+ emoji: reaction.emoji
2877
+ }
2878
+ };
2879
+ listeners.forEach((fn) => fn(message));
2850
2880
  },
2851
2881
  onConnectionStateChanged: (state) => {
2852
2882
  connectionStateListeners.current.forEach(
@@ -2890,6 +2920,7 @@ var MeetingProvider = ({
2890
2920
  ...config,
2891
2921
  ...joinConfig
2892
2922
  }),
2923
+ getJoinState: (roomCode) => sdk.getJoinState(roomCode),
2893
2924
  leave: () => sdk.disconnect(),
2894
2925
  createRoom: sdk.createRoom.bind(sdk),
2895
2926
  getRoom: sdk.getRoom.bind(sdk),
@@ -2908,12 +2939,48 @@ var MeetingProvider = ({
2908
2939
  activeSpeakerIds,
2909
2940
  recording,
2910
2941
  usePubSub: (topic) => {
2911
- if (topic !== "SECURE_CHAT") {
2912
- throw new Error(`Unsupported PubSub argument: "${topic}"`);
2942
+ if (topic === "REACTION") {
2943
+ return {
2944
+ messages: [],
2945
+ publish: (data) => {
2946
+ const reaction = data;
2947
+ if (!reaction?.emoji) {
2948
+ console.warn("[PubSub] REACTION requires an emoji");
2949
+ return;
2950
+ }
2951
+ sdk.sendReaction(reaction.emoji);
2952
+ }
2953
+ };
2954
+ }
2955
+ if (topic === "SECURE_CHAT") {
2956
+ return {
2957
+ messages: sdk.state.getChatMessages().map((message) => ({
2958
+ id: message.id,
2959
+ topic: "SECURE_CHAT",
2960
+ sender_id: message.sender_id,
2961
+ sender_name: message.sender_name,
2962
+ timestamp: message.timestamp,
2963
+ data: message
2964
+ })),
2965
+ publish: (data) => {
2966
+ sdk.sendChatMessage(data);
2967
+ }
2968
+ };
2969
+ }
2970
+ throw new Error(`Unsupported PubSub topic: "${topic}"`);
2971
+ },
2972
+ onPubSubMessage: (topic, cb) => {
2973
+ let listeners = pubSubListeners.current.get(topic);
2974
+ if (!listeners) {
2975
+ listeners = /* @__PURE__ */ new Set();
2976
+ pubSubListeners.current.set(topic, listeners);
2913
2977
  }
2914
- return {
2915
- messages: sdk.state.getChatMessages(),
2916
- publish: sdk.sendChatMessage.bind(sdk)
2978
+ listeners.add(cb);
2979
+ return () => {
2980
+ listeners?.delete(cb);
2981
+ if (listeners?.size === 0) {
2982
+ pubSubListeners.current.delete(topic);
2983
+ }
2917
2984
  };
2918
2985
  },
2919
2986
  approveJoinRequest: sdk.approveJoinRequest.bind(sdk),
@@ -3212,6 +3279,17 @@ function useMeetingPreview(roomId, participantId) {
3212
3279
  error
3213
3280
  };
3214
3281
  }
3282
+
3283
+ // src/react/usePubSub.ts
3284
+ var import_react8 = require("react");
3285
+ function usePubSub(topic, options) {
3286
+ const ctx = useMeetingContext();
3287
+ (0, import_react8.useEffect)(() => {
3288
+ if (!options?.onMessageReceived) return;
3289
+ return ctx.onPubSubMessage(topic, options.onMessageReceived);
3290
+ }, [ctx, topic, options?.onMessageReceived]);
3291
+ return ctx.usePubSub(topic);
3292
+ }
3215
3293
  // Annotate the CommonJS export names for ESM import in node:
3216
3294
  0 && (module.exports = {
3217
3295
  ActiveSpeakerDetector,
@@ -3223,6 +3301,7 @@ function useMeetingPreview(roomId, participantId) {
3223
3301
  useMeetingContext,
3224
3302
  useMeetingPreview,
3225
3303
  useParticipants,
3304
+ usePubSub,
3226
3305
  useRemoteMedia
3227
3306
  });
3228
3307
  //# sourceMappingURL=index.js.map