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