@stream-io/video-react-sdk 1.32.3 → 1.33.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/css/embedded.css +3630 -0
  3. package/dist/css/embedded.css.map +1 -0
  4. package/dist/css/styles.css +13 -2
  5. package/dist/css/styles.css.map +1 -1
  6. package/dist/embedded-BackgroundFilters-RdXfNf6_.es.js +353 -0
  7. package/dist/embedded-BackgroundFilters-RdXfNf6_.es.js.map +1 -0
  8. package/dist/embedded-BackgroundFilters-Zu84SkRR.cjs.js +355 -0
  9. package/dist/embedded-BackgroundFilters-Zu84SkRR.cjs.js.map +1 -0
  10. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js +57 -0
  11. package/dist/embedded-CallStatsLatencyChart-Bj5OSYzg.es.js.map +1 -0
  12. package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js +59 -0
  13. package/dist/embedded-CallStatsLatencyChart-CpL1M_s0.cjs.js.map +1 -0
  14. package/dist/embedded.cjs.js +3410 -0
  15. package/dist/embedded.cjs.js.map +1 -0
  16. package/dist/embedded.d.ts +1 -0
  17. package/dist/embedded.es.js +3407 -0
  18. package/dist/embedded.es.js.map +1 -0
  19. package/dist/index.cjs.js +67 -202
  20. package/dist/index.cjs.js.map +1 -1
  21. package/dist/index.es.js +69 -204
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/src/embedded/EmbeddedClientProvider.d.ts +21 -0
  24. package/dist/src/embedded/call/CallControls.d.ts +9 -0
  25. package/dist/src/embedded/call/CallHeader.d.ts +4 -0
  26. package/dist/src/embedded/call/CallLayout.d.ts +4 -0
  27. package/dist/src/embedded/call/CallStateRouter.d.ts +4 -0
  28. package/dist/src/embedded/call/EmbeddedCall.d.ts +6 -0
  29. package/dist/src/embedded/call/index.d.ts +1 -0
  30. package/dist/src/embedded/context/ConfigurationContext.d.ts +11 -0
  31. package/dist/src/embedded/context/index.d.ts +1 -0
  32. package/dist/src/embedded/hooks/index.d.ts +8 -0
  33. package/dist/src/embedded/hooks/useCallDuration.d.ts +7 -0
  34. package/dist/src/embedded/hooks/useEmbeddedClient.d.ts +22 -0
  35. package/dist/src/embedded/hooks/useInitializeCall.d.ts +11 -0
  36. package/dist/src/embedded/hooks/useInitializeVideoClient.d.ts +16 -0
  37. package/dist/src/embedded/hooks/useIsLivestreamPaused.d.ts +8 -0
  38. package/dist/src/embedded/hooks/useLayout.d.ts +9 -0
  39. package/dist/src/embedded/hooks/useNoiseCancellationLoader.d.ts +12 -0
  40. package/dist/src/embedded/hooks/useWakeLock.d.ts +5 -0
  41. package/dist/src/embedded/index.d.ts +3 -0
  42. package/dist/src/embedded/livestream/EmbeddedLivestream.d.ts +5 -0
  43. package/dist/src/embedded/livestream/LivestreamUI.d.ts +1 -0
  44. package/dist/src/embedded/livestream/host/HostLayout.d.ts +7 -0
  45. package/dist/src/embedded/livestream/host/HostStateRouter.d.ts +1 -0
  46. package/dist/src/embedded/livestream/index.d.ts +1 -0
  47. package/dist/src/embedded/livestream/viewer/ViewerLayout.d.ts +1 -0
  48. package/dist/src/embedded/livestream/viewer/ViewerLobby.d.ts +4 -0
  49. package/dist/src/embedded/livestream/viewer/ViewerStateRouter.d.ts +1 -0
  50. package/dist/src/embedded/shared/BlurToggleButton/BlurToggleButton.d.ts +2 -0
  51. package/dist/src/embedded/shared/CallFeedback/CallEndedScreen.d.ts +6 -0
  52. package/dist/src/embedded/shared/CallFeedback/CallFeedback.d.ts +4 -0
  53. package/dist/src/embedded/shared/CallFeedback/RatingScreen.d.ts +5 -0
  54. package/dist/src/embedded/shared/CallFeedback/StarRating.d.ts +6 -0
  55. package/dist/src/embedded/shared/CallFeedback/ThankYouScreen.d.ts +1 -0
  56. package/dist/src/embedded/shared/ConnectionNotification/ConnectionNotification.d.ts +1 -0
  57. package/dist/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.d.ts +1 -0
  58. package/dist/src/embedded/shared/JoinError/JoinError.d.ts +5 -0
  59. package/dist/src/embedded/shared/Lobby/DeviceControls.d.ts +5 -0
  60. package/dist/src/embedded/shared/Lobby/DisabledDeviceButton.d.ts +6 -0
  61. package/dist/src/embedded/shared/Lobby/Lobby.d.ts +10 -0
  62. package/dist/src/embedded/shared/Lobby/ToggleCameraButton.d.ts +1 -0
  63. package/dist/src/embedded/shared/Lobby/ToggleMicButton.d.ts +1 -0
  64. package/dist/src/embedded/shared/Lobby/VideoPreviewFallbacks.d.ts +2 -0
  65. package/dist/src/embedded/shared/ViewersCount/ViewersCount.d.ts +5 -0
  66. package/dist/src/embedded/shared/index.d.ts +7 -0
  67. package/dist/src/embedded/types.d.ts +65 -0
  68. package/dist/src/hooks/usePersistedDevicePreferences.d.ts +3 -12
  69. package/dist/src/translations/index.d.ts +42 -1
  70. package/embedded.ts +1 -0
  71. package/package.json +18 -4
  72. package/src/core/components/CallLayout/LivestreamLayout.tsx +53 -41
  73. package/src/embedded/EmbeddedClientProvider.tsx +125 -0
  74. package/src/embedded/call/CallControls.tsx +124 -0
  75. package/src/embedded/call/CallHeader.tsx +30 -0
  76. package/src/embedded/call/CallLayout.tsx +66 -0
  77. package/src/embedded/call/CallStateRouter.tsx +56 -0
  78. package/src/embedded/call/EmbeddedCall.tsx +14 -0
  79. package/src/embedded/call/index.ts +1 -0
  80. package/src/embedded/context/ConfigurationContext.tsx +36 -0
  81. package/src/embedded/context/index.ts +1 -0
  82. package/src/embedded/hooks/index.ts +8 -0
  83. package/src/embedded/hooks/useCallDuration.ts +40 -0
  84. package/src/embedded/hooks/useEmbeddedClient.ts +64 -0
  85. package/src/embedded/hooks/useInitializeCall.ts +51 -0
  86. package/src/embedded/hooks/useInitializeVideoClient.ts +118 -0
  87. package/src/embedded/hooks/useIsLivestreamPaused.ts +44 -0
  88. package/src/embedded/hooks/useLayout.ts +100 -0
  89. package/src/embedded/hooks/useNoiseCancellationLoader.ts +62 -0
  90. package/src/embedded/hooks/useWakeLock.ts +33 -0
  91. package/src/embedded/index.ts +12 -0
  92. package/src/embedded/livestream/EmbeddedLivestream.tsx +16 -0
  93. package/src/embedded/livestream/LivestreamUI.tsx +17 -0
  94. package/src/embedded/livestream/host/HostLayout.tsx +210 -0
  95. package/src/embedded/livestream/host/HostStateRouter.tsx +100 -0
  96. package/src/embedded/livestream/index.ts +1 -0
  97. package/src/embedded/livestream/viewer/ViewerLayout.tsx +160 -0
  98. package/src/embedded/livestream/viewer/ViewerLobby.tsx +135 -0
  99. package/src/embedded/livestream/viewer/ViewerStateRouter.tsx +82 -0
  100. package/src/embedded/shared/BlurToggleButton/BlurToggleButton.tsx +75 -0
  101. package/src/embedded/shared/CallFeedback/CallEndedScreen.tsx +55 -0
  102. package/src/embedded/shared/CallFeedback/CallFeedback.tsx +51 -0
  103. package/src/embedded/shared/CallFeedback/RatingScreen.tsx +47 -0
  104. package/src/embedded/shared/CallFeedback/StarRating.tsx +46 -0
  105. package/src/embedded/shared/CallFeedback/ThankYouScreen.tsx +19 -0
  106. package/src/embedded/shared/ConnectionNotification/ConnectionNotification.tsx +59 -0
  107. package/src/embedded/shared/EmbeddedParticipantViewUI/EmbeddedParticipantViewUI.tsx +32 -0
  108. package/src/embedded/shared/JoinError/JoinError.tsx +27 -0
  109. package/src/embedded/shared/Lobby/DeviceControls.tsx +54 -0
  110. package/src/embedded/shared/Lobby/DisabledDeviceButton.tsx +21 -0
  111. package/src/embedded/shared/Lobby/Lobby.tsx +59 -0
  112. package/src/embedded/shared/Lobby/ToggleCameraButton.tsx +44 -0
  113. package/src/embedded/shared/Lobby/ToggleMicButton.tsx +48 -0
  114. package/src/embedded/shared/Lobby/VideoPreviewFallbacks.tsx +55 -0
  115. package/src/embedded/shared/ViewersCount/ViewersCount.tsx +18 -0
  116. package/src/embedded/shared/index.ts +7 -0
  117. package/src/embedded/types.ts +80 -0
  118. package/src/hooks/usePersistedDevicePreferences.ts +8 -307
  119. package/src/translations/en.json +44 -2
@@ -0,0 +1,47 @@
1
+ import { useState } from 'react';
2
+ import { useI18n } from '@stream-io/video-react-bindings';
3
+ import { StarRating } from './StarRating';
4
+
5
+ interface RatingScreenProps {
6
+ onSubmit: (rating: number, message: string) => void;
7
+ }
8
+
9
+ export const RatingScreen = ({ onSubmit }: RatingScreenProps) => {
10
+ const { t } = useI18n();
11
+ const [rating, setRating] = useState(0);
12
+ const [message, setMessage] = useState('');
13
+
14
+ const handleSubmit = () => {
15
+ if (rating > 0) onSubmit(rating, message);
16
+ };
17
+
18
+ return (
19
+ <div className="str-video__embedded-call-feedback__container">
20
+ <h2 className="str-video__embedded-call-feedback__title">
21
+ {t('Share your feedback')}
22
+ </h2>
23
+
24
+ <StarRating value={rating} onChange={setRating} />
25
+
26
+ <textarea
27
+ aria-label={t('Feedback message')}
28
+ className="str-video__embedded-call-feedback__textarea"
29
+ placeholder={t('Tell us about your experience...')}
30
+ value={message}
31
+ onChange={(e) => setMessage(e.target.value)}
32
+ rows={3}
33
+ />
34
+
35
+ <div className="str-video__embedded-call-feedback__actions">
36
+ <button
37
+ type="button"
38
+ className="str-video__button"
39
+ onClick={handleSubmit}
40
+ disabled={rating === 0}
41
+ >
42
+ {t('Submit feedback')}
43
+ </button>
44
+ </div>
45
+ </div>
46
+ );
47
+ };
@@ -0,0 +1,46 @@
1
+ import { useState } from 'react';
2
+ import clsx from 'clsx';
3
+ import { useI18n } from '@stream-io/video-react-bindings';
4
+ import { Icon } from '../../../components';
5
+
6
+ interface StarRatingProps {
7
+ value: number;
8
+ onChange: (rating: number) => void;
9
+ }
10
+
11
+ export const StarRating = ({ value, onChange }: StarRatingProps) => {
12
+ const { t } = useI18n();
13
+ const [hovered, setHovered] = useState(0);
14
+ const displayValue = hovered || value;
15
+
16
+ const getStarClasses = (star: number) =>
17
+ clsx(
18
+ 'str-video__embedded-call-feedback__star',
19
+ star <= displayValue && 'str-video__embedded-call-feedback__star--active',
20
+ );
21
+
22
+ return (
23
+ <div className="str-video__embedded-call-feedback__rating-section">
24
+ <p className="str-video__embedded-call-feedback__rating-label">
25
+ {t('How was your call quality?')}
26
+ </p>
27
+ <div
28
+ className="str-video__embedded-call-feedback__stars"
29
+ onMouseLeave={() => setHovered(0)}
30
+ >
31
+ {[1, 2, 3, 4, 5].map((star) => (
32
+ <button
33
+ key={star}
34
+ type="button"
35
+ className={getStarClasses(star)}
36
+ onClick={() => onChange(star)}
37
+ onMouseEnter={() => setHovered(star)}
38
+ aria-label={t('Rate {{ count }} star', { count: star })}
39
+ >
40
+ <Icon icon="star-filled" />
41
+ </button>
42
+ ))}
43
+ </div>
44
+ </div>
45
+ );
46
+ };
@@ -0,0 +1,19 @@
1
+ import { useI18n } from '@stream-io/video-react-bindings';
2
+ import { Icon } from '../../../components';
3
+
4
+ export const ThankYouScreen = () => {
5
+ const { t } = useI18n();
6
+ return (
7
+ <div className="str-video__embedded-call-feedback__container">
8
+ <div className="str-video__embedded-call-feedback__checkmark">
9
+ <Icon icon="checkmark" />
10
+ </div>
11
+ <h2 className="str-video__embedded-call-feedback__title">
12
+ {t('Thank you!')}
13
+ </h2>
14
+ <p className="str-video__embedded-call-feedback__subtitle">
15
+ {t('Your feedback helps improve call quality.')}
16
+ </p>
17
+ </div>
18
+ );
19
+ };
@@ -0,0 +1,59 @@
1
+ import { CallingState } from '@stream-io/video-client';
2
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
3
+ import { LoadingIndicator, Notification } from '../../../components';
4
+
5
+ export const ConnectionNotification = () => {
6
+ const { t } = useI18n();
7
+ const { useCallCallingState } = useCallStateHooks();
8
+ const callingState = useCallCallingState();
9
+
10
+ const isOffline = callingState === CallingState.OFFLINE;
11
+ const isReconnecting = callingState === CallingState.RECONNECTING;
12
+ const isMigrating = callingState === CallingState.MIGRATING;
13
+ const isJoining = callingState === CallingState.JOINING;
14
+ const hasFailedToRecover = callingState === CallingState.RECONNECTING_FAILED;
15
+
16
+ const showError = isOffline || hasFailedToRecover;
17
+ const showLoading = isJoining || isReconnecting || isMigrating;
18
+
19
+ if (showError) {
20
+ return (
21
+ <div className="str-video__embedded-connection-notification">
22
+ <Notification
23
+ isVisible
24
+ placement="bottom"
25
+ message={
26
+ isOffline
27
+ ? t('You are offline. Check your internet connection.')
28
+ : t('Failed to restore connection. Please try again.')
29
+ }
30
+ />
31
+ </div>
32
+ );
33
+ }
34
+
35
+ if (showLoading) {
36
+ return (
37
+ <div className="str-video__embedded-connection-notification">
38
+ <Notification
39
+ isVisible
40
+ placement="bottom"
41
+ iconClassName={null}
42
+ message={
43
+ <LoadingIndicator
44
+ text={
45
+ isMigrating
46
+ ? t('Migrating...')
47
+ : isJoining
48
+ ? t('Joining')
49
+ : t('Reconnecting...')
50
+ }
51
+ />
52
+ }
53
+ />
54
+ </div>
55
+ );
56
+ }
57
+
58
+ return null;
59
+ };
@@ -0,0 +1,32 @@
1
+ import { useCallback } from 'react';
2
+ import clsx from 'clsx';
3
+ import { DefaultParticipantViewUI } from '../../../core';
4
+ import { useParticipantViewContext } from '../../../core';
5
+
6
+ export const EmbeddedParticipantViewUI = () => {
7
+ const { participantViewElement, trackType } = useParticipantViewContext();
8
+
9
+ const handleDoubleClick = useCallback(() => {
10
+ if (!participantViewElement) return;
11
+ if (typeof participantViewElement.requestFullscreen === 'undefined') return;
12
+
13
+ if (!document.fullscreenElement) {
14
+ participantViewElement.requestFullscreen().catch(console.error);
15
+ } else {
16
+ document.exitFullscreen().catch(console.error);
17
+ }
18
+ }, [participantViewElement]);
19
+
20
+ return (
21
+ <div
22
+ className={clsx(
23
+ 'str-video__embedded-participant-view-ui',
24
+ trackType === 'screenShareTrack' &&
25
+ 'str-video__embedded-participant-view-ui--screen-share',
26
+ )}
27
+ onDoubleClick={handleDoubleClick}
28
+ >
29
+ <DefaultParticipantViewUI />
30
+ </div>
31
+ );
32
+ };
@@ -0,0 +1,27 @@
1
+ import { useI18n } from '@stream-io/video-react-bindings';
2
+ import { Icon } from '../../../components';
3
+
4
+ interface JoinErrorProps {
5
+ onJoin: () => void;
6
+ }
7
+
8
+ export const JoinError = ({ onJoin }: JoinErrorProps) => {
9
+ const { t } = useI18n();
10
+
11
+ return (
12
+ <div className="str-video__embedded-join-error">
13
+ <h2 className="str-video__embedded-join-error__title">
14
+ {t('Failed to join the call')}
15
+ </h2>
16
+ <p className="str-video__embedded-join-error__message">
17
+ {t(
18
+ "We couldn't connect to the server. Please check your connection and try again.",
19
+ )}
20
+ </p>
21
+ <button type="button" className="str-video__button" onClick={onJoin}>
22
+ <Icon icon="login" />
23
+ {t('Try again')}
24
+ </button>
25
+ </div>
26
+ );
27
+ };
@@ -0,0 +1,54 @@
1
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
2
+ import {
3
+ ToggleAudioPreviewButton,
4
+ ToggleVideoPreviewButton,
5
+ VideoPreview,
6
+ } from '../../../components';
7
+ import { ToggleMicButton } from './ToggleMicButton';
8
+ import { ToggleCameraButton } from './ToggleCameraButton';
9
+ import { DisabledDeviceButton } from './DisabledDeviceButton';
10
+ import { DisabledVideoPreview, NoCameraPreview } from './VideoPreviewFallbacks';
11
+
12
+ interface DeviceControlsProps {
13
+ isVideoEnabled: boolean;
14
+ }
15
+
16
+ export const DeviceControls = ({ isVideoEnabled }: DeviceControlsProps) => {
17
+ const { t } = useI18n();
18
+ const { useCameraState, useMicrophoneState } = useCallStateHooks();
19
+
20
+ const { hasBrowserPermission: hasCameraPermission } = useCameraState();
21
+ const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
22
+
23
+ return (
24
+ <>
25
+ <div className="str-video__embedded-lobby__video-preview">
26
+ <VideoPreview
27
+ DisabledVideoPreview={DisabledVideoPreview}
28
+ NoCameraPreview={NoCameraPreview}
29
+ />
30
+ <div className="str-video__embedded-lobby__media-toggle">
31
+ <ToggleAudioPreviewButton Menu={null} />
32
+ {isVideoEnabled && <ToggleVideoPreviewButton Menu={null} />}
33
+ </div>
34
+ </div>
35
+
36
+ <div className="str-video__embedded-lobby__media">
37
+ {hasMicPermission ? (
38
+ <ToggleMicButton />
39
+ ) : (
40
+ <DisabledDeviceButton icon="mic" label={t('Permission needed')} />
41
+ )}
42
+ {isVideoEnabled &&
43
+ (hasCameraPermission ? (
44
+ <ToggleCameraButton />
45
+ ) : (
46
+ <DisabledDeviceButton
47
+ icon="camera"
48
+ label={t('Permission needed')}
49
+ />
50
+ ))}
51
+ </div>
52
+ </>
53
+ );
54
+ };
@@ -0,0 +1,21 @@
1
+ import { Icon } from '../../../components';
2
+
3
+ interface DisabledDeviceButtonProps {
4
+ icon: string;
5
+ label: string;
6
+ }
7
+
8
+ export const DisabledDeviceButton = ({
9
+ icon,
10
+ label,
11
+ }: DisabledDeviceButtonProps) => (
12
+ <div className="str-video__embedded-lobby__device-button str-video__embedded-lobby__device-button--disabled">
13
+ <Icon
14
+ className="str-video__embedded-lobby__device-button-icon"
15
+ icon={icon}
16
+ />
17
+ <span className="str-video__embedded-lobby__device-button-label">
18
+ {label}
19
+ </span>
20
+ </div>
21
+ );
@@ -0,0 +1,59 @@
1
+ import clsx from 'clsx';
2
+ import {
3
+ useCallStateHooks,
4
+ useConnectedUser,
5
+ useI18n,
6
+ } from '@stream-io/video-react-bindings';
7
+ import { Icon } from '../../../components';
8
+ import { DeviceControls } from './DeviceControls';
9
+
10
+ interface LobbyProps {
11
+ onJoin: () => void;
12
+ title?: string;
13
+ joinLabel?: string;
14
+ }
15
+
16
+ /**
17
+ * Lobby component - Device setup screen before joining a call.
18
+ */
19
+ export const Lobby = ({ onJoin, title, joinLabel }: LobbyProps) => {
20
+ const { t } = useI18n();
21
+ const user = useConnectedUser();
22
+ const { useCameraState, useCallSettings } = useCallStateHooks();
23
+
24
+ const { isMute } = useCameraState();
25
+ const settings = useCallSettings();
26
+
27
+ const isVideoEnabled = settings?.video.enabled ?? true;
28
+ const resolvedJoinLabel = joinLabel ?? t('Join');
29
+ const resolvedTitle = title ?? t('Set up your call before joining');
30
+
31
+ return (
32
+ <div className="str-video__embedded-lobby">
33
+ <div className="str-video__embedded-lobby__content">
34
+ <h1 className="str-video__embedded-lobby__heading">{resolvedTitle}</h1>
35
+ <div
36
+ className={clsx(
37
+ 'str-video__embedded-lobby__camera',
38
+ isMute && 'str-video__embedded-lobby__camera--off',
39
+ )}
40
+ >
41
+ <DeviceControls isVideoEnabled={isVideoEnabled} />
42
+ </div>
43
+
44
+ <div className="str-video__embedded-lobby__display-name">
45
+ <div className="str-video__embedded-lobby__display-name-label">
46
+ {t('Display name')}
47
+ </div>
48
+ <span className="str-video__embedded-lobby__display-name-value">
49
+ {user?.name}
50
+ </span>
51
+ <button className="str-video__button" onClick={onJoin}>
52
+ <Icon className="str-video__button__icon" icon="login" />
53
+ {resolvedJoinLabel}
54
+ </button>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ );
59
+ };
@@ -0,0 +1,44 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import { useCallStateHooks } from '@stream-io/video-react-bindings';
4
+ import {
5
+ Icon,
6
+ MenuToggle,
7
+ MenuVisualType,
8
+ ToggleMenuButtonProps,
9
+ } from '../../../components';
10
+ import { CameraMenuWithBlur } from '../BlurToggleButton/BlurToggleButton';
11
+
12
+ const ToggleMenuButton = forwardRef<HTMLButtonElement, ToggleMenuButtonProps>(
13
+ function ToggleMenuButton(props, ref) {
14
+ const { useCameraState } = useCallStateHooks();
15
+ const { selectedDevice: selectedCamera, devices: cameras } =
16
+ useCameraState();
17
+
18
+ return (
19
+ <button ref={ref} className="str-video__embedded-lobby__device-button">
20
+ <Icon
21
+ className="str-video__embedded-lobby__device-button-icon"
22
+ icon="camera"
23
+ />
24
+ <span className="str-video__embedded-lobby__device-button-label">
25
+ {cameras?.find((c: MediaDeviceInfo) => c.deviceId === selectedCamera)
26
+ ?.label || 'Default'}
27
+ </span>
28
+ <Icon icon={props.menuShown ? 'chevron-down' : 'chevron-up'} />
29
+ </button>
30
+ );
31
+ },
32
+ );
33
+
34
+ export const ToggleCameraButton = () => {
35
+ return (
36
+ <MenuToggle
37
+ placement="top-start"
38
+ ToggleButton={ToggleMenuButton}
39
+ visualType={MenuVisualType.MENU}
40
+ >
41
+ <CameraMenuWithBlur />
42
+ </MenuToggle>
43
+ );
44
+ };
@@ -0,0 +1,48 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
4
+ import {
5
+ DeviceSelectorAudioInput,
6
+ DeviceSelectorAudioOutput,
7
+ Icon,
8
+ MenuToggle,
9
+ MenuVisualType,
10
+ ToggleMenuButtonProps,
11
+ } from '../../../components';
12
+
13
+ const ToggleMenuButton = forwardRef<HTMLButtonElement, ToggleMenuButtonProps>(
14
+ function ToggleMenuButton(props, ref) {
15
+ const { useMicrophoneState } = useCallStateHooks();
16
+ const { selectedDevice: selectedMic, devices: microphones } =
17
+ useMicrophoneState();
18
+
19
+ return (
20
+ <button ref={ref} className="str-video__embedded-lobby__device-button">
21
+ <Icon
22
+ className="str-video__embedded-lobby__device-button-icon"
23
+ icon="mic"
24
+ />
25
+ <span className="str-video__embedded-lobby__device-button-label">
26
+ {microphones?.find((m: MediaDeviceInfo) => m.deviceId === selectedMic)
27
+ ?.label || 'Default'}
28
+ </span>
29
+ <Icon icon={props.menuShown ? 'chevron-down' : 'chevron-up'} />
30
+ </button>
31
+ );
32
+ },
33
+ );
34
+
35
+ export const ToggleMicButton = () => {
36
+ const { t } = useI18n();
37
+
38
+ return (
39
+ <MenuToggle
40
+ placement="top-start"
41
+ ToggleButton={ToggleMenuButton}
42
+ visualType={MenuVisualType.MENU}
43
+ >
44
+ <DeviceSelectorAudioInput visualType="list" title={t('Microphone')} />
45
+ <DeviceSelectorAudioOutput visualType="list" title={t('Speaker')} />
46
+ </MenuToggle>
47
+ );
48
+ };
@@ -0,0 +1,55 @@
1
+ import {
2
+ useCallStateHooks,
3
+ useConnectedUser,
4
+ useI18n,
5
+ } from '@stream-io/video-react-bindings';
6
+ import { Avatar } from '../../../components';
7
+
8
+ export const DisabledVideoPreview = () => {
9
+ const { t } = useI18n();
10
+
11
+ const user = useConnectedUser();
12
+ const { useCameraState, useMicrophoneState } = useCallStateHooks();
13
+
14
+ const { hasBrowserPermission: hasCameraPermission } = useCameraState();
15
+ const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
16
+
17
+ const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
18
+
19
+ return (
20
+ <div className="str-video__embedded-lobby__no-permission">
21
+ {hasBrowserMediaPermission ? (
22
+ <Avatar imageSrc={user?.image} name={user?.name || user?.id} />
23
+ ) : (
24
+ t(
25
+ 'Please grant your browser permission to access your camera and microphone.',
26
+ )
27
+ )}
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export const NoCameraPreview = () => {
33
+ const { t } = useI18n();
34
+ const { useCameraState, useMicrophoneState } = useCallStateHooks();
35
+ const { hasBrowserPermission: hasCameraPermission } = useCameraState();
36
+ const { hasBrowserPermission: hasMicPermission } = useMicrophoneState();
37
+
38
+ const hasBrowserMediaPermission = hasCameraPermission && hasMicPermission;
39
+
40
+ if (!hasBrowserMediaPermission) {
41
+ return (
42
+ <div className="str-video__embedded-lobby__no-permission">
43
+ {t(
44
+ 'Please grant your browser permission to access your camera and microphone.',
45
+ )}
46
+ </div>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <div className="str-video__video-preview__no-camera-preview">
52
+ {t('No camera found')}
53
+ </div>
54
+ );
55
+ };
@@ -0,0 +1,18 @@
1
+ import { humanize } from '@stream-io/video-client';
2
+ import { Icon } from '../../../components';
3
+
4
+ interface ViewersCountProps {
5
+ count: number;
6
+ }
7
+
8
+ export const ViewersCount = ({ count }: ViewersCountProps) => (
9
+ <div className="str-video__embedded-livestream-duration__viewers">
10
+ <Icon
11
+ icon="eye"
12
+ className="str-video__embedded-livestream-duration__eye-icon"
13
+ />
14
+ <span className="str-video__embedded-livestream-duration__count">
15
+ {humanize(count)}
16
+ </span>
17
+ </div>
18
+ );
@@ -0,0 +1,7 @@
1
+ export * from './BlurToggleButton/BlurToggleButton';
2
+ export * from './CallFeedback/CallFeedback';
3
+ export * from './ConnectionNotification/ConnectionNotification';
4
+ export * from './EmbeddedParticipantViewUI/EmbeddedParticipantViewUI';
5
+ export * from './JoinError/JoinError';
6
+ export * from './Lobby/Lobby';
7
+ export * from './ViewersCount/ViewersCount';
@@ -0,0 +1,80 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { LogLevel, TokenProvider } from '@stream-io/video-client';
3
+
4
+ /**
5
+ * Available layout options.
6
+ */
7
+ export type LayoutOption =
8
+ | 'Livestream'
9
+ | 'PaginatedGrid'
10
+ | 'SpeakerLeft'
11
+ | 'SpeakerRight'
12
+ | 'SpeakerTop'
13
+ | 'SpeakerBottom';
14
+
15
+ /**
16
+ * An authenticated user with a known identity.
17
+ * Requires a `token` or `tokenProvider` on the component props.
18
+ */
19
+ export interface EmbeddedAuthenticatedUser {
20
+ type?: 'authenticated';
21
+ id: string;
22
+ name?: string;
23
+ image?: string;
24
+ }
25
+
26
+ /**
27
+ * A guest user — the server generates credentials automatically.
28
+ */
29
+ export interface EmbeddedGuestUser {
30
+ type: 'guest';
31
+ id: string;
32
+ name?: string;
33
+ image?: string;
34
+ }
35
+
36
+ /**
37
+ * An anonymous user with no identity.
38
+ * May optionally receive a call-scoped token via the `token` prop.
39
+ */
40
+ export interface EmbeddedAnonymousUser {
41
+ type: 'anonymous';
42
+ id?: '!anon';
43
+ name?: string;
44
+ image?: string;
45
+ }
46
+
47
+ /**
48
+ * Discriminated union for embedded user configuration.
49
+ */
50
+ export type EmbeddedUser =
51
+ | EmbeddedAuthenticatedUser
52
+ | EmbeddedGuestUser
53
+ | EmbeddedAnonymousUser;
54
+
55
+ /**
56
+ * Base props shared by EmbeddedCall and EmbeddedLivestream.
57
+ */
58
+ export interface EmbeddedClientBaseProps {
59
+ apiKey: string;
60
+ callType: string;
61
+ callId: string;
62
+ user: EmbeddedUser;
63
+ token?: string;
64
+ tokenProvider?: TokenProvider;
65
+ logLevel?: LogLevel;
66
+ layout?: LayoutOption;
67
+ theme?: Record<string, string>;
68
+ onError?: (error: any) => void;
69
+ children?: ReactNode;
70
+ }
71
+
72
+ /**
73
+ * Props for the EmbeddedCall component.
74
+ */
75
+ export interface EmbeddedMeetingProps extends EmbeddedClientBaseProps {}
76
+
77
+ /**
78
+ * Props for the EmbeddedLivestream component.
79
+ */
80
+ export interface EmbeddedLivestreamProps extends EmbeddedClientBaseProps {}