@poolse/react 2.13.1 → 2.14.1

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.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia } from '@poolse/sdk';
2
+ import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, CallDeviceKind, CallDevice, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia } from '@poolse/sdk';
3
3
  export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, Conversation, ConversationCreateRequest, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, RealtimeStatus, Uuid } from '@poolse/sdk';
4
4
  import { ReactNode } from 'react';
5
5
 
@@ -518,6 +518,31 @@ interface UseCallRoom {
518
518
  micEnabled: boolean;
519
519
  /** Which camera is publishing. Meaningless while the camera is off. */
520
520
  facingMode: CameraFacing;
521
+ /**
522
+ * Selectable devices, refreshed as they are plugged in and removed.
523
+ *
524
+ * The web's answer to a phone's speaker button — a laptop has no
525
+ * earpiece, but it does have several microphones and a pair of
526
+ * headphones, and choosing between them is the same job. Sparse on
527
+ * React Native, where routing goes through the platform audio session
528
+ * instead (see `useCallAudio` in `@poolse/react-native`).
529
+ */
530
+ devices: Record<CallDeviceKind, CallDevice[]>;
531
+ /** Device id in use per kind, where the platform reports one. */
532
+ activeDevices: Partial<Record<CallDeviceKind, string>>;
533
+ /** Move the call onto another camera, microphone or output. */
534
+ selectDevice: (kind: CallDeviceKind, deviceId: string) => Promise<void>;
535
+ /**
536
+ * Re-read the device list.
537
+ *
538
+ * Called for you on connect and whenever devices change, so this is
539
+ * for the case neither covers: a browser only reveals device ids and
540
+ * labels once the page holds a matching permission, and the moment
541
+ * that permission lands is not an event you can subscribe to. Call it
542
+ * when opening a picker and the list will be right by the time it is
543
+ * on screen.
544
+ */
545
+ refreshDevices: () => void;
521
546
  /**
522
547
  * Everyone else has gone and is not coming back.
523
548
  *
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia } from '@poolse/sdk';
2
+ import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, CallDeviceKind, CallDevice, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia } from '@poolse/sdk';
3
3
  export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, Conversation, ConversationCreateRequest, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, RealtimeStatus, Uuid } from '@poolse/sdk';
4
4
  import { ReactNode } from 'react';
5
5
 
@@ -518,6 +518,31 @@ interface UseCallRoom {
518
518
  micEnabled: boolean;
519
519
  /** Which camera is publishing. Meaningless while the camera is off. */
520
520
  facingMode: CameraFacing;
521
+ /**
522
+ * Selectable devices, refreshed as they are plugged in and removed.
523
+ *
524
+ * The web's answer to a phone's speaker button — a laptop has no
525
+ * earpiece, but it does have several microphones and a pair of
526
+ * headphones, and choosing between them is the same job. Sparse on
527
+ * React Native, where routing goes through the platform audio session
528
+ * instead (see `useCallAudio` in `@poolse/react-native`).
529
+ */
530
+ devices: Record<CallDeviceKind, CallDevice[]>;
531
+ /** Device id in use per kind, where the platform reports one. */
532
+ activeDevices: Partial<Record<CallDeviceKind, string>>;
533
+ /** Move the call onto another camera, microphone or output. */
534
+ selectDevice: (kind: CallDeviceKind, deviceId: string) => Promise<void>;
535
+ /**
536
+ * Re-read the device list.
537
+ *
538
+ * Called for you on connect and whenever devices change, so this is
539
+ * for the case neither covers: a browser only reveals device ids and
540
+ * labels once the page holds a matching permission, and the moment
541
+ * that permission lands is not an event you can subscribe to. Call it
542
+ * when opening a picker and the list will be right by the time it is
543
+ * on screen.
544
+ */
545
+ refreshDevices: () => void;
521
546
  /**
522
547
  * Everyone else has gone and is not coming back.
523
548
  *
package/dist/index.js CHANGED
@@ -1070,6 +1070,12 @@ function useCallRoom(conversationId, opts) {
1070
1070
  const [micEnabled, setMic] = useState(false);
1071
1071
  const [facingMode, setFacing] = useState(opts.facing ?? "user");
1072
1072
  const [alone, setAlone] = useState(false);
1073
+ const [devices, setDevices] = useState({
1074
+ videoinput: [],
1075
+ audioinput: [],
1076
+ audiooutput: []
1077
+ });
1078
+ const [activeDevices, setActiveDevices] = useState({});
1073
1079
  const [error, setError] = useState(null);
1074
1080
  const [sfuAvailable, setSfuAvailable] = useState(null);
1075
1081
  const optsRef = useRef(opts);
@@ -1085,6 +1091,21 @@ function useCallRoom(conversationId, opts) {
1085
1091
  ...optsRef.current.aloneGraceMs !== void 0 ? { aloneGraceMs: optsRef.current.aloneGraceMs } : {}
1086
1092
  });
1087
1093
  }, [poolse, conversationId]);
1094
+ const refreshDevices = useCallback((target) => {
1095
+ void Promise.all([
1096
+ target.listDevices("videoinput"),
1097
+ target.listDevices("audioinput"),
1098
+ target.listDevices("audiooutput")
1099
+ ]).then(([videoinput, audioinput, audiooutput]) => {
1100
+ setDevices({ videoinput, audioinput, audiooutput });
1101
+ const active = {};
1102
+ for (const kind of ["videoinput", "audioinput", "audiooutput"]) {
1103
+ const id = target.getActiveDevice(kind);
1104
+ if (id) active[kind] = id;
1105
+ }
1106
+ setActiveDevices(active);
1107
+ });
1108
+ }, []);
1088
1109
  const sync = useCallback((target) => {
1089
1110
  if (!target) return;
1090
1111
  setCamera(target.isCameraEnabled());
@@ -1106,18 +1127,20 @@ function useCallRoom(conversationId, opts) {
1106
1127
  sync(room);
1107
1128
  });
1108
1129
  const offAlone = room.onAlone(() => setAlone(true));
1130
+ const offDevices = room.onDevices(() => refreshDevices(room));
1109
1131
  const offError = room.onError(setError);
1110
1132
  return () => {
1111
1133
  offStatus();
1112
1134
  offRoster();
1113
1135
  offAlone();
1136
+ offDevices();
1114
1137
  offError();
1115
1138
  if (joined.current) {
1116
1139
  joined.current = false;
1117
1140
  room.leave();
1118
1141
  }
1119
1142
  };
1120
- }, [room, sync]);
1143
+ }, [room, sync, refreshDevices]);
1121
1144
  const join = useCallback(async () => {
1122
1145
  if (!room) return false;
1123
1146
  setError(null);
@@ -1126,13 +1149,14 @@ function useCallRoom(conversationId, opts) {
1126
1149
  joined.current = connected;
1127
1150
  setSfuAvailable(connected);
1128
1151
  sync(room);
1152
+ if (connected) refreshDevices(room);
1129
1153
  return connected;
1130
1154
  } catch (err) {
1131
1155
  setError(err instanceof Error ? err : new Error(String(err)));
1132
1156
  setSfuAvailable(true);
1133
1157
  return false;
1134
1158
  }
1135
- }, [room, sync]);
1159
+ }, [room, sync, refreshDevices]);
1136
1160
  const leave = useCallback(() => {
1137
1161
  if (!joined.current) return;
1138
1162
  joined.current = false;
@@ -1167,6 +1191,20 @@ function useCallRoom(conversationId, opts) {
1167
1191
  await room.switchCamera();
1168
1192
  setFacing(room.getFacingMode());
1169
1193
  }, [room]);
1194
+ const refresh = useCallback(() => {
1195
+ if (room) refreshDevices(room);
1196
+ }, [room, refreshDevices]);
1197
+ const selectDevice = useCallback(
1198
+ async (kind, deviceId) => {
1199
+ if (!room) return;
1200
+ await room.setActiveDevice(kind, deviceId);
1201
+ setActiveDevices((current) => ({
1202
+ ...current,
1203
+ [kind]: room.getActiveDevice(kind) ?? deviceId
1204
+ }));
1205
+ },
1206
+ [room]
1207
+ );
1170
1208
  return {
1171
1209
  status,
1172
1210
  participants,
@@ -1174,6 +1212,9 @@ function useCallRoom(conversationId, opts) {
1174
1212
  micEnabled,
1175
1213
  facingMode,
1176
1214
  alone,
1215
+ devices,
1216
+ activeDevices,
1217
+ refreshDevices: refresh,
1177
1218
  error,
1178
1219
  sfuAvailable,
1179
1220
  join,
@@ -1181,6 +1222,7 @@ function useCallRoom(conversationId, opts) {
1181
1222
  setCameraEnabled,
1182
1223
  toggleCamera,
1183
1224
  switchCamera,
1225
+ selectDevice,
1184
1226
  setMicrophoneEnabled,
1185
1227
  toggleMic
1186
1228
  };