@stream-io/video-react-sdk 1.33.3 → 1.34.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/dist/index.cjs.js CHANGED
@@ -1347,7 +1347,7 @@ const SpeakerTest = (props) => {
1347
1347
  const audioElementRef = react.useRef(null);
1348
1348
  const [isPlaying, setIsPlaying] = react.useState(false);
1349
1349
  const { t } = videoReactBindings.useI18n();
1350
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.33.3"}/assets/piano.mp3`, } = props;
1350
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.34.0"}/assets/piano.mp3`, } = props;
1351
1351
  // Update audio output device when selection changes
1352
1352
  react.useEffect(() => {
1353
1353
  const audio = audioElementRef.current;
@@ -1582,7 +1582,7 @@ const CancelCallButton = ({ disabled, caption, onClick, onLeave, }) => {
1582
1582
  return (jsxRuntime.jsx(IconButton, { disabled: disabled, icon: "call-end", variant: "danger", title: caption ?? t('Leave call'), "data-testid": "cancel-call-button", onClick: handleClick }));
1583
1583
  };
1584
1584
 
1585
- const CallControls = ({ onLeave }) => (jsxRuntime.jsxs("div", { className: "str-video__call-controls", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], children: jsxRuntime.jsx(MicCaptureErrorNotification, { children: jsxRuntime.jsx(SpeakingWhileMutedNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, {}) }) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], children: jsxRuntime.jsx(ToggleVideoPublishingButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx(ReactionsButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx(ScreenShareButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [
1585
+ const CallControls = ({ onLeave }) => (jsxRuntime.jsxs("div", { className: "str-video__call-controls", children: [jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], children: jsxRuntime.jsx(SpeakingWhileMutedNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, {}) }) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], children: jsxRuntime.jsx(ToggleVideoPublishingButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.CREATE_REACTION], children: jsxRuntime.jsx(ReactionsButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx(ScreenShareButton, {}) }), jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [
1586
1586
  videoClient.OwnCapability.START_RECORD_CALL,
1587
1587
  videoClient.OwnCapability.STOP_RECORD_CALL,
1588
1588
  ], children: jsxRuntime.jsx(RecordCallButton, {}) }), jsxRuntime.jsx(CancelCallButton, { onLeave: onLeave })] }));
@@ -2024,6 +2024,7 @@ const NoiseCancellationProvider = (props) => {
2024
2024
  }, [noiseCancellation]);
2025
2025
  const isSupported = isSupportedByBrowser && hasCapability && noiseCancellationAllowed;
2026
2026
  const [isEnabled, setIsEnabled] = react.useState(false);
2027
+ const [isReady, setIsReady] = react.useState(false);
2027
2028
  const deinit = react.useRef(undefined);
2028
2029
  react.useEffect(() => {
2029
2030
  if (!call || !isSupported)
@@ -2032,17 +2033,23 @@ const NoiseCancellationProvider = (props) => {
2032
2033
  const unsubscribe = noiseCancellation.on('change', (v) => setIsEnabled(v));
2033
2034
  const init = (deinit.current || Promise.resolve())
2034
2035
  .then(() => noiseCancellation.init({ tracer: call.tracer }))
2035
- .then(() => call.microphone.enableNoiseCancellation(noiseCancellation))
2036
+ .then(() => {
2037
+ setIsReady(true);
2038
+ return call.microphone.enableNoiseCancellation(noiseCancellation);
2039
+ })
2036
2040
  .catch((e) => console.error(`Can't initialize noise cancellation`, e));
2037
2041
  return () => {
2038
2042
  deinit.current = init
2039
2043
  .then(() => call.microphone.disableNoiseCancellation())
2040
2044
  .then(() => noiseCancellation.dispose())
2041
- .then(() => unsubscribe());
2045
+ .then(() => unsubscribe())
2046
+ .catch((e) => console.error("Can't clean up noise cancellation", e))
2047
+ .finally(() => setIsReady(false));
2042
2048
  };
2043
2049
  }, [call, isSupported, noiseCancellation]);
2044
2050
  const contextValue = react.useMemo(() => ({
2045
2051
  isSupported,
2052
+ isReady,
2046
2053
  isEnabled,
2047
2054
  setSuppressionLevel: (level) => {
2048
2055
  if (!noiseCancellation)
@@ -2066,7 +2073,7 @@ const NoiseCancellationProvider = (props) => {
2066
2073
  });
2067
2074
  }
2068
2075
  },
2069
- }), [isEnabled, isSupported, noiseCancellation]);
2076
+ }), [isEnabled, isReady, isSupported, noiseCancellation]);
2070
2077
  return (jsxRuntime.jsx(NoiseCancellationContext.Provider, { value: contextValue, children: children }));
2071
2078
  };
2072
2079
 
@@ -2281,7 +2288,9 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
2281
2288
  const hasVideoTrack = videoClient.hasVideo(participant);
2282
2289
  const canUnpin = !!pin && pin.isLocalPin;
2283
2290
  const isTrackPaused = trackType !== 'none' ? videoClient.hasPausedTrack(participant, trackType) : false;
2284
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "str-video__participant-details", children: jsxRuntime.jsxs("span", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && !hasAudioTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && !hasVideoTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsxRuntime.jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && canUnpin && (
2291
+ const isAudioTrackUnmuted = useIsTrackUnmuted(participant);
2292
+ const isAudioConnecting = hasAudioTrack && !isAudioTrackUnmuted;
2293
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "str-video__participant-details", children: jsxRuntime.jsxs("div", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isAudioConnecting && (jsxRuntime.jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && !hasVideoTrack && (jsxRuntime.jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsxRuntime.jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && canUnpin && (
2285
2294
  // TODO: remove this monstrosity once we have a proper design
2286
2295
  jsxRuntime.jsx("span", { title: t('Unpin'), onClick: () => call?.unpin(sessionId), className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsxRuntime.jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsxRuntime.jsx(Notification, { isVisible: isLocalParticipant &&
2287
2296
  connectionQuality === videoClient.SfuModels.ConnectionQuality.POOR, message: t('Poor connection quality'), children: connectionQualityAsString && (jsxRuntime.jsx("span", { className: clsx('str-video__participant-details__connection-quality', `str-video__participant-details__connection-quality--${connectionQualityAsString}`), title: connectionQualityAsString })) }))] }));
@@ -2291,6 +2300,29 @@ const SpeechIndicator = () => {
2291
2300
  const { isSpeaking, isDominantSpeaker } = participant;
2292
2301
  return (jsxRuntime.jsxs("span", { className: clsx('str-video__speech-indicator', isSpeaking && 'str-video__speech-indicator--speaking', isDominantSpeaker && 'str-video__speech-indicator--dominant'), children: [jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" }), jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" }), jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" })] }));
2293
2302
  };
2303
+ const useIsTrackUnmuted = (participant) => {
2304
+ const audioStream = participant.audioStream;
2305
+ const [unmuted, setUnmuted] = react.useState(() => {
2306
+ const track = audioStream?.getAudioTracks()[0];
2307
+ return !!track && !track.muted;
2308
+ });
2309
+ react.useEffect(() => {
2310
+ const track = audioStream?.getAudioTracks()[0];
2311
+ if (!track)
2312
+ return;
2313
+ setUnmuted(!track.muted);
2314
+ const handler = () => {
2315
+ setUnmuted(!track.muted);
2316
+ };
2317
+ track.addEventListener('mute', handler);
2318
+ track.addEventListener('unmute', handler);
2319
+ return () => {
2320
+ track.removeEventListener('mute', handler);
2321
+ track.removeEventListener('unmute', handler);
2322
+ };
2323
+ }, [audioStream]);
2324
+ return unmuted;
2325
+ };
2294
2326
 
2295
2327
  const ParticipantView = react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
2296
2328
  const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
@@ -2461,6 +2493,7 @@ var en = {
2461
2493
  "Dominant Speaker": "Dominant Speaker",
2462
2494
  "Poor connection quality": "Poor connection quality. Please check your internet connection.",
2463
2495
  "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
2496
+ "Audio is connecting...": "Audio is connecting...",
2464
2497
  Participants: Participants,
2465
2498
  Anonymous: Anonymous,
2466
2499
  "No participants found": "No participants found",
@@ -3137,7 +3170,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
3137
3170
  return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
3138
3171
  };
3139
3172
 
3140
- const [major, minor, patch] = ("1.33.3").split('.');
3173
+ const [major, minor, patch] = ("1.34.0").split('.');
3141
3174
  videoClient.setSdkInfo({
3142
3175
  type: videoClient.SfuModels.SdkType.REACT,
3143
3176
  major,