@stream-io/video-react-sdk 1.26.1 → 1.27.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.
@@ -0,0 +1,75 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
3
+ import { CompositeButton } from '../Button';
4
+ import { Icon } from '../Icon';
5
+
6
+ /**
7
+ * SpeakerTest component that plays a test audio through the selected speaker.
8
+ * This allows users to verify their audio output device is working correctly.
9
+ */
10
+ export const SpeakerTest = (props: { audioUrl?: string }) => {
11
+ const { useSpeakerState } = useCallStateHooks();
12
+ const { selectedDevice } = useSpeakerState();
13
+ const audioElementRef = useRef<HTMLAudioElement | null>(null);
14
+ const [isPlaying, setIsPlaying] = useState(false);
15
+ const { t } = useI18n();
16
+
17
+ const {
18
+ audioUrl = `https://unpkg.com/${process.env.PKG_NAME}@${process.env.PKG_VERSION}/assets/piano.mp3`,
19
+ } = props;
20
+
21
+ // Update audio output device when selection changes
22
+ useEffect(() => {
23
+ const audio = audioElementRef.current;
24
+ if (!audio || !selectedDevice) return;
25
+
26
+ // Set the sinkId to route audio to the selected speaker
27
+ if ('setSinkId' in audio) {
28
+ audio.setSinkId(selectedDevice).catch((err) => {
29
+ console.error('Failed to set audio output device:', err);
30
+ });
31
+ }
32
+ }, [selectedDevice]);
33
+
34
+ const handleStartTest = useCallback(async () => {
35
+ const audio = audioElementRef.current;
36
+ if (!audio) return;
37
+
38
+ audio.src = audioUrl;
39
+
40
+ try {
41
+ if (isPlaying) {
42
+ audio.pause();
43
+ audio.currentTime = 0;
44
+ setIsPlaying(false);
45
+ } else {
46
+ await audio.play();
47
+ setIsPlaying(true);
48
+ }
49
+ } catch (err) {
50
+ console.error('Failed to play test audio:', err);
51
+ setIsPlaying(false);
52
+ }
53
+ }, [isPlaying, audioUrl]);
54
+
55
+ const handleAudioEnded = useCallback(() => setIsPlaying(false), []);
56
+ return (
57
+ <div className="str-video__speaker-test">
58
+ <audio
59
+ ref={audioElementRef}
60
+ onEnded={handleAudioEnded}
61
+ onPause={handleAudioEnded}
62
+ />
63
+ <CompositeButton
64
+ className="str-video__speaker-test__button"
65
+ onClick={handleStartTest}
66
+ type="button"
67
+ >
68
+ <div className="str-video__speaker-test__button-content">
69
+ <Icon icon="speaker" />
70
+ {isPlaying ? t('Stop test') : t('Test speaker')}
71
+ </div>
72
+ </CompositeButton>
73
+ </div>
74
+ );
75
+ };
@@ -3,3 +3,4 @@ export * from './DeviceSettings';
3
3
  export * from './DeviceSelector';
4
4
  export * from './DeviceSelectorAudio';
5
5
  export * from './DeviceSelectorVideo';
6
+ export * from './SpeakerTest';
@@ -1,6 +1,6 @@
1
1
  import { PropsWithChildren, ReactNode, useEffect } from 'react';
2
+ import clsx from 'clsx';
2
3
  import { Placement } from '@floating-ui/react';
3
-
4
4
  import { useFloatingUIPreset } from '../../hooks';
5
5
 
6
6
  export type NotificationProps = {
@@ -9,6 +9,7 @@ export type NotificationProps = {
9
9
  visibilityTimeout?: number;
10
10
  resetIsVisible?: () => void;
11
11
  placement?: Placement;
12
+ className?: string;
12
13
  iconClassName?: string | null;
13
14
  close?: () => void;
14
15
  };
@@ -21,6 +22,7 @@ export const Notification = (props: PropsWithChildren<NotificationProps>) => {
21
22
  visibilityTimeout,
22
23
  resetIsVisible,
23
24
  placement = 'top',
25
+ className,
24
26
  iconClassName = 'str-video__notification__icon',
25
27
  close,
26
28
  } = props;
@@ -44,7 +46,7 @@ export const Notification = (props: PropsWithChildren<NotificationProps>) => {
44
46
  <div ref={refs.setReference}>
45
47
  {isVisible && (
46
48
  <div
47
- className="str-video__notification"
49
+ className={clsx('str-video__notification', className)}
48
50
  ref={refs.setFloating}
49
51
  style={{
50
52
  position: strategy,
@@ -11,6 +11,7 @@
11
11
  "Speakers": "Speakers",
12
12
  "Video": "Video",
13
13
  "You are muted. Unmute to speak.": "You are muted. Unmute to speak.",
14
+ "Background filters performance is degraded. Consider disabling filters for better performance.": "Background filters performance is degraded. Consider disabling filters for better performance.",
14
15
 
15
16
  "Live": "Live",
16
17
  "Livestream starts soon": "Livestream starts soon",