@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.
@@ -1153,7 +1153,7 @@ const SpeakerTest = (props) => {
1153
1153
  const audioElementRef = react.useRef(null);
1154
1154
  const [isPlaying, setIsPlaying] = react.useState(false);
1155
1155
  const { t } = videoReactBindings.useI18n();
1156
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.33.3"}/assets/piano.mp3`, } = props;
1156
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.34.0"}/assets/piano.mp3`, } = props;
1157
1157
  // Update audio output device when selection changes
1158
1158
  react.useEffect(() => {
1159
1159
  const audio = audioElementRef.current;
@@ -1572,6 +1572,7 @@ const NoiseCancellationProvider = (props) => {
1572
1572
  }, [noiseCancellation]);
1573
1573
  const isSupported = isSupportedByBrowser && hasCapability && noiseCancellationAllowed;
1574
1574
  const [isEnabled, setIsEnabled] = react.useState(false);
1575
+ const [isReady, setIsReady] = react.useState(false);
1575
1576
  const deinit = react.useRef(undefined);
1576
1577
  react.useEffect(() => {
1577
1578
  if (!call || !isSupported)
@@ -1580,17 +1581,23 @@ const NoiseCancellationProvider = (props) => {
1580
1581
  const unsubscribe = noiseCancellation.on('change', (v) => setIsEnabled(v));
1581
1582
  const init = (deinit.current || Promise.resolve())
1582
1583
  .then(() => noiseCancellation.init({ tracer: call.tracer }))
1583
- .then(() => call.microphone.enableNoiseCancellation(noiseCancellation))
1584
+ .then(() => {
1585
+ setIsReady(true);
1586
+ return call.microphone.enableNoiseCancellation(noiseCancellation);
1587
+ })
1584
1588
  .catch((e) => console.error(`Can't initialize noise cancellation`, e));
1585
1589
  return () => {
1586
1590
  deinit.current = init
1587
1591
  .then(() => call.microphone.disableNoiseCancellation())
1588
1592
  .then(() => noiseCancellation.dispose())
1589
- .then(() => unsubscribe());
1593
+ .then(() => unsubscribe())
1594
+ .catch((e) => console.error("Can't clean up noise cancellation", e))
1595
+ .finally(() => setIsReady(false));
1590
1596
  };
1591
1597
  }, [call, isSupported, noiseCancellation]);
1592
1598
  const contextValue = react.useMemo(() => ({
1593
1599
  isSupported,
1600
+ isReady,
1594
1601
  isEnabled,
1595
1602
  setSuppressionLevel: (level) => {
1596
1603
  if (!noiseCancellation)
@@ -1614,7 +1621,7 @@ const NoiseCancellationProvider = (props) => {
1614
1621
  });
1615
1622
  }
1616
1623
  },
1617
- }), [isEnabled, isSupported, noiseCancellation]);
1624
+ }), [isEnabled, isReady, isSupported, noiseCancellation]);
1618
1625
  return (jsxRuntime.jsx(NoiseCancellationContext.Provider, { value: contextValue, children: children }));
1619
1626
  };
1620
1627
 
@@ -1787,7 +1794,9 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
1787
1794
  const hasVideoTrack = videoClient.hasVideo(participant);
1788
1795
  const canUnpin = !!pin && pin.isLocalPin;
1789
1796
  const isTrackPaused = trackType !== 'none' ? videoClient.hasPausedTrack(participant, trackType) : false;
1790
- 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 && (
1797
+ const isAudioTrackUnmuted = useIsTrackUnmuted(participant);
1798
+ const isAudioConnecting = hasAudioTrack && !isAudioTrackUnmuted;
1799
+ 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 && (
1791
1800
  // TODO: remove this monstrosity once we have a proper design
1792
1801
  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 &&
1793
1802
  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 })) }))] }));
@@ -1797,6 +1806,29 @@ const SpeechIndicator = () => {
1797
1806
  const { isSpeaking, isDominantSpeaker } = participant;
1798
1807
  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" })] }));
1799
1808
  };
1809
+ const useIsTrackUnmuted = (participant) => {
1810
+ const audioStream = participant.audioStream;
1811
+ const [unmuted, setUnmuted] = react.useState(() => {
1812
+ const track = audioStream?.getAudioTracks()[0];
1813
+ return !!track && !track.muted;
1814
+ });
1815
+ react.useEffect(() => {
1816
+ const track = audioStream?.getAudioTracks()[0];
1817
+ if (!track)
1818
+ return;
1819
+ setUnmuted(!track.muted);
1820
+ const handler = () => {
1821
+ setUnmuted(!track.muted);
1822
+ };
1823
+ track.addEventListener('mute', handler);
1824
+ track.addEventListener('unmute', handler);
1825
+ return () => {
1826
+ track.removeEventListener('mute', handler);
1827
+ track.removeEventListener('unmute', handler);
1828
+ };
1829
+ }, [audioStream]);
1830
+ return unmuted;
1831
+ };
1800
1832
 
1801
1833
  const ParticipantView = react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
1802
1834
  const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
@@ -1967,6 +1999,7 @@ var en = {
1967
1999
  "Dominant Speaker": "Dominant Speaker",
1968
2000
  "Poor connection quality": "Poor connection quality. Please check your internet connection.",
1969
2001
  "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
2002
+ "Audio is connecting...": "Audio is connecting...",
1970
2003
  Participants: Participants,
1971
2004
  Anonymous: Anonymous,
1972
2005
  "No participants found": "No participants found",