@stream-io/video-react-sdk 1.34.2 → 1.35.0
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/CHANGELOG.md +13 -0
- package/dist/css/embedded.css +54 -0
- package/dist/css/embedded.css.map +1 -1
- package/dist/css/styles.css +54 -0
- package/dist/css/styles.css.map +1 -1
- package/dist/embedded.cjs.js +90 -3
- package/dist/embedded.cjs.js.map +1 -1
- package/dist/embedded.es.js +90 -3
- package/dist/embedded.es.js.map +1 -1
- package/dist/index.cjs.js +91 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +91 -4
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/DeviceSettings/DeviceAudioPreviewItem.d.ts +6 -0
- package/dist/src/components/DeviceSettings/DeviceSelector.d.ts +11 -2
- package/dist/src/components/DeviceSettings/DeviceSelectorAudio.d.ts +1 -1
- package/dist/src/components/DeviceSettings/DeviceSelectorVideo.d.ts +1 -1
- package/dist/src/components/DeviceSettings/DeviceVideoPreviewItem.d.ts +6 -0
- package/package.json +5 -5
- package/src/components/DeviceSettings/DeviceAudioPreviewItem.tsx +83 -0
- package/src/components/DeviceSettings/DeviceSelector.tsx +70 -5
- package/src/components/DeviceSettings/DeviceSelectorAudio.tsx +3 -1
- package/src/components/DeviceSettings/DeviceSelectorVideo.tsx +5 -2
- package/src/components/DeviceSettings/DeviceVideoPreviewItem.tsx +73 -0
package/dist/embedded.cjs.js
CHANGED
|
@@ -1036,6 +1036,41 @@ const AudioVolumeIndicator = () => {
|
|
|
1036
1036
|
return (jsxRuntime.jsxs("div", { className: "str-video__audio-volume-indicator", children: [jsxRuntime.jsx(Icon, { icon: isEnabled ? 'mic' : 'mic-off' }), jsxRuntime.jsx("div", { className: "str-video__audio-volume-indicator__bar", children: jsxRuntime.jsx("div", { className: "str-video__audio-volume-indicator__bar-value", style: { transform: `scaleX(${audioLevel / 100})` } }) })] }));
|
|
1037
1037
|
};
|
|
1038
1038
|
|
|
1039
|
+
const LEVEL_BARS = 5;
|
|
1040
|
+
const DeviceLevelIndicator = ({ deviceId }) => {
|
|
1041
|
+
const [audioLevel, setAudioLevel] = react.useState(0);
|
|
1042
|
+
react.useEffect(() => {
|
|
1043
|
+
let cancelled = false;
|
|
1044
|
+
let dispose;
|
|
1045
|
+
navigator.mediaDevices
|
|
1046
|
+
.getUserMedia({
|
|
1047
|
+
audio: { deviceId: { exact: deviceId } },
|
|
1048
|
+
video: false,
|
|
1049
|
+
})
|
|
1050
|
+
.then((mediaStream) => {
|
|
1051
|
+
if (cancelled) {
|
|
1052
|
+
mediaStream.getTracks().forEach((t) => t.stop());
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
dispose = videoClient.createSoundDetector(mediaStream, ({ audioLevel: al }) => setAudioLevel(al), { detectionFrequencyInMs: 80 });
|
|
1056
|
+
})
|
|
1057
|
+
.catch(console.error);
|
|
1058
|
+
return () => {
|
|
1059
|
+
cancelled = true;
|
|
1060
|
+
dispose?.().catch(console.error);
|
|
1061
|
+
};
|
|
1062
|
+
}, [deviceId]);
|
|
1063
|
+
const activeBars = Math.round((audioLevel / 100) * LEVEL_BARS);
|
|
1064
|
+
return (jsxRuntime.jsx("div", { className: "str-video__device-level-indicator", "aria-label": "Audio level", children: Array.from({ length: LEVEL_BARS }, (_, i) => (jsxRuntime.jsx("div", { className: clsx('str-video__device-level-indicator__bar', {
|
|
1065
|
+
'str-video__device-level-indicator__bar--active': i < activeBars,
|
|
1066
|
+
}) }, i))) }));
|
|
1067
|
+
};
|
|
1068
|
+
const DeviceAudioPreviewItem = ({ device, onSelect, }) => {
|
|
1069
|
+
if (device.deviceId === 'default')
|
|
1070
|
+
return null;
|
|
1071
|
+
return (jsxRuntime.jsxs("label", { className: `str-video__device-settings__option${device.isSelected ? ' str-video__device-settings__option--selected' : ''}`, htmlFor: `audioinput--${device.deviceId}`, children: [jsxRuntime.jsx("input", { type: "radio", name: "audioinput", value: device.deviceId, id: `audioinput--${device.deviceId}`, checked: device.isSelected, onChange: (e) => onSelect(e.target.value) }), device.label, jsxRuntime.jsx(DeviceLevelIndicator, { deviceId: device.deviceId })] }));
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1039
1074
|
const SelectContext = react.createContext({});
|
|
1040
1075
|
const Select = (props) => {
|
|
1041
1076
|
const { children, icon, defaultSelectedLabel, defaultSelectedIndex, handleSelect: handleSelectProp, } = props;
|
|
@@ -1127,6 +1162,18 @@ const DeviceSelectorList = (props) => {
|
|
|
1127
1162
|
}, name: type, selected: device.isSelected }, device.deviceId));
|
|
1128
1163
|
}), children] }));
|
|
1129
1164
|
};
|
|
1165
|
+
const DeviceSelectorPreview = (props) => {
|
|
1166
|
+
const { devices = [], selectedDeviceId, title, onChange, children, PreviewItem, } = props;
|
|
1167
|
+
const { close } = useMenuContext();
|
|
1168
|
+
const { deviceList } = useDeviceList(devices, selectedDeviceId);
|
|
1169
|
+
const onSelect = react.useCallback((deviceId) => {
|
|
1170
|
+
if (deviceId === 'default')
|
|
1171
|
+
return;
|
|
1172
|
+
onChange?.(deviceId);
|
|
1173
|
+
close?.();
|
|
1174
|
+
}, [onChange, close]);
|
|
1175
|
+
return (jsxRuntime.jsxs("div", { className: "str-video__device-settings__device-kind", children: [title && (jsxRuntime.jsx("div", { className: "str-video__device-settings__device-selector-title", children: title })), deviceList.map((device) => (jsxRuntime.jsx(PreviewItem, { device: device, onSelect: onSelect }, device.deviceId))), children] }));
|
|
1176
|
+
};
|
|
1130
1177
|
const DeviceSelectorDropdown = (props) => {
|
|
1131
1178
|
const { devices = [], selectedDeviceId, title, onChange, icon } = props;
|
|
1132
1179
|
const { deviceList, selectedDeviceInfo, selectedIndex } = useDeviceList(devices, selectedDeviceId);
|
|
@@ -1139,6 +1186,10 @@ const DeviceSelectorDropdown = (props) => {
|
|
|
1139
1186
|
return (jsxRuntime.jsxs("div", { className: "str-video__device-settings__device-kind", children: [jsxRuntime.jsx("div", { className: "str-video__device-settings__device-selector-title", children: title }), jsxRuntime.jsx(DropDownSelect, { icon: icon, defaultSelectedIndex: selectedIndex, defaultSelectedLabel: selectedDeviceInfo.label, handleSelect: handleSelect, children: deviceList.map((device) => (jsxRuntime.jsx(DropDownSelectOption, { icon: icon, label: device.label, selected: device.isSelected }, device.deviceId))) })] }));
|
|
1140
1187
|
};
|
|
1141
1188
|
const DeviceSelector = (props) => {
|
|
1189
|
+
if (props.visualType === 'preview') {
|
|
1190
|
+
const { PreviewItem, ...rest } = props;
|
|
1191
|
+
return jsxRuntime.jsx(DeviceSelectorPreview, { ...rest, PreviewItem: PreviewItem });
|
|
1192
|
+
}
|
|
1142
1193
|
const { visualType = 'list', icon, ...rest } = props;
|
|
1143
1194
|
if (visualType === 'list') {
|
|
1144
1195
|
return jsxRuntime.jsx(DeviceSelectorList, { ...rest });
|
|
@@ -1156,7 +1207,7 @@ const SpeakerTest = (props) => {
|
|
|
1156
1207
|
const audioElementRef = react.useRef(null);
|
|
1157
1208
|
const [isPlaying, setIsPlaying] = react.useState(false);
|
|
1158
1209
|
const { t } = videoReactBindings.useI18n();
|
|
1159
|
-
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.
|
|
1210
|
+
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.35.0"}/assets/piano.mp3`, } = props;
|
|
1160
1211
|
// Update audio output device when selection changes
|
|
1161
1212
|
react.useEffect(() => {
|
|
1162
1213
|
const audio = audioElementRef.current;
|
|
@@ -1199,7 +1250,7 @@ const DeviceSelectorAudioInput = ({ title, visualType, volumeIndicatorVisible =
|
|
|
1199
1250
|
const { microphone, selectedDevice, devices } = useMicrophoneState();
|
|
1200
1251
|
return (jsxRuntime.jsx(DeviceSelector, { devices: devices || [], selectedDeviceId: selectedDevice, type: "audioinput", onChange: async (deviceId) => {
|
|
1201
1252
|
await microphone.select(deviceId);
|
|
1202
|
-
}, title: title, visualType: visualType, icon: "mic", children: volumeIndicatorVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" }), jsxRuntime.jsx(AudioVolumeIndicator, {})] })) }));
|
|
1253
|
+
}, title: title, visualType: visualType, icon: "mic", PreviewItem: DeviceAudioPreviewItem, children: volumeIndicatorVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" }), jsxRuntime.jsx(AudioVolumeIndicator, {})] })) }));
|
|
1203
1254
|
};
|
|
1204
1255
|
const DeviceSelectorAudioOutput = ({ title, visualType, speakerTestVisible = true, speakerTestAudioUrl, }) => {
|
|
1205
1256
|
const { useSpeakerState } = videoReactBindings.useCallStateHooks();
|
|
@@ -1211,12 +1262,48 @@ const DeviceSelectorAudioOutput = ({ title, visualType, speakerTestVisible = tru
|
|
|
1211
1262
|
}, title: title, visualType: visualType, icon: "speaker", children: speakerTestVisible && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("hr", { className: "str-video__device-settings__separator" }), jsxRuntime.jsx(SpeakerTest, { audioUrl: speakerTestAudioUrl })] })) }));
|
|
1212
1263
|
};
|
|
1213
1264
|
|
|
1265
|
+
const DeviceVideoPreview = ({ deviceId }) => {
|
|
1266
|
+
const videoRef = react.useRef(null);
|
|
1267
|
+
react.useEffect(() => {
|
|
1268
|
+
let cancelled = false;
|
|
1269
|
+
let stream;
|
|
1270
|
+
navigator.mediaDevices
|
|
1271
|
+
.getUserMedia({
|
|
1272
|
+
video: { deviceId: { exact: deviceId } },
|
|
1273
|
+
audio: false,
|
|
1274
|
+
})
|
|
1275
|
+
.then((mediaStream) => {
|
|
1276
|
+
if (cancelled) {
|
|
1277
|
+
mediaStream.getTracks().forEach((t) => t.stop());
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
stream = mediaStream;
|
|
1281
|
+
if (videoRef.current) {
|
|
1282
|
+
videoRef.current.srcObject = mediaStream;
|
|
1283
|
+
}
|
|
1284
|
+
})
|
|
1285
|
+
.catch(console.error);
|
|
1286
|
+
return () => {
|
|
1287
|
+
cancelled = true;
|
|
1288
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
1289
|
+
};
|
|
1290
|
+
}, [deviceId]);
|
|
1291
|
+
return (jsxRuntime.jsx("div", { className: "str-video__device-video-preview", children: jsxRuntime.jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "str-video__device-video-preview__video" }) }));
|
|
1292
|
+
};
|
|
1293
|
+
const DeviceVideoPreviewItem = ({ device, onSelect, }) => {
|
|
1294
|
+
if (device.deviceId === 'default')
|
|
1295
|
+
return null;
|
|
1296
|
+
return (jsxRuntime.jsxs("button", { type: "button", className: clsx('str-video__device-preview', {
|
|
1297
|
+
'str-video__device-preview--selected': device.isSelected,
|
|
1298
|
+
}), onClick: () => onSelect(device.deviceId), "aria-pressed": device.isSelected, children: [jsxRuntime.jsx(DeviceVideoPreview, { deviceId: device.deviceId }), jsxRuntime.jsx("span", { className: "str-video__device-preview__label", children: device.label })] }));
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1214
1301
|
const DeviceSelectorVideo = ({ title, visualType, }) => {
|
|
1215
1302
|
const { useCameraState } = videoReactBindings.useCallStateHooks();
|
|
1216
1303
|
const { camera, devices, selectedDevice } = useCameraState();
|
|
1217
1304
|
return (jsxRuntime.jsx(DeviceSelector, { devices: devices || [], type: "videoinput", selectedDeviceId: selectedDevice, onChange: async (deviceId) => {
|
|
1218
1305
|
await camera.select(deviceId);
|
|
1219
|
-
}, title: title, visualType: visualType, icon: "camera" }));
|
|
1306
|
+
}, title: title, visualType: visualType, icon: "camera", PreviewItem: DeviceVideoPreviewItem }));
|
|
1220
1307
|
};
|
|
1221
1308
|
|
|
1222
1309
|
react.forwardRef(function ToggleDeviceSettingsMenuButton({ menuShown }, ref) {
|