@stream-io/video-react-sdk 1.36.1 → 1.37.1
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/CHANGELOG.md +17 -0
- package/dist/css/embedded.css +60 -54
- package/dist/css/embedded.css.map +1 -1
- package/dist/css/styles.css +50 -44
- package/dist/css/styles.css.map +1 -1
- package/dist/embedded.cjs.js +40 -24
- package/dist/embedded.cjs.js.map +1 -1
- package/dist/embedded.es.js +41 -25
- package/dist/embedded.es.js.map +1 -1
- package/dist/index.cjs.js +54 -26
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +54 -26
- package/dist/index.es.js.map +1 -1
- package/dist/src/core/hooks/useTrackElementVisibility.d.ts +2 -3
- package/dist/src/translations/index.d.ts +2 -0
- package/package.json +5 -5
- package/src/components/CallControls/ToggleAudioButton.tsx +32 -5
- package/src/components/CallControls/ToggleVideoButton.tsx +36 -5
- package/src/core/components/CallLayout/LivestreamLayout.tsx +17 -1
- package/src/core/components/CallLayout/PaginatedGridLayout.tsx +1 -1
- package/src/core/components/CallLayout/SpeakerLayout.tsx +1 -1
- package/src/core/components/ParticipantView/ParticipantView.tsx +0 -1
- package/src/core/hooks/useTrackElementVisibility.ts +4 -14
- package/src/translations/en.json +2 -0
package/dist/embedded.es.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, isValidElement, forwardRef, useLayoutEffect, lazy, Suspense } from 'react';
|
|
3
3
|
import { useCall, useCallStateHooks, useI18n, Restricted, useToggleCallRecording, useConnectedUser, useIsAudioConnecting, StreamCallProvider, StreamVideoProvider, useEffectEvent } from '@stream-io/video-react-bindings';
|
|
4
|
-
import { hasAudio, hasScreenShareAudio, hasVideo, hasScreenShare, OwnCapability, Browsers, VisibilityState, hasPausedTrack, createSoundDetector, isPinned, name, NoiseCancellationSettingsModeEnum, CallingState,
|
|
4
|
+
import { hasAudio, hasScreenShareAudio, hasVideo, hasScreenShare, OwnCapability, Browsers, VisibilityState, hasPausedTrack, createSoundDetector, SfuModels, isPinned, name, NoiseCancellationSettingsModeEnum, CallingState, combineComparators, screenSharing, speakerLayoutSortPreset, paginatedLayoutSortPreset, CallTypes, defaultSortPreset, humanize, StreamVideoClient } from '@stream-io/video-client';
|
|
5
5
|
import { useFloating, offset, shift, flip, size, autoUpdate, FloatingOverlay, FloatingPortal, arrow, FloatingArrow, useListItem, useListNavigation, useTypeahead, useClick, useDismiss, useRole, useInteractions, FloatingFocusManager, FloatingList } from '@floating-ui/react';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
7
|
|
|
@@ -663,17 +663,13 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
|
|
|
663
663
|
};
|
|
664
664
|
Video$1.displayName = 'Video';
|
|
665
665
|
|
|
666
|
-
const useTrackElementVisibility = ({ trackedElement,
|
|
666
|
+
const useTrackElementVisibility = ({ trackedElement, sessionId, trackType, }) => {
|
|
667
667
|
const call = useCall();
|
|
668
|
-
const manager = propsDynascaleManager ?? call?.dynascaleManager;
|
|
669
668
|
useEffect(() => {
|
|
670
|
-
if (!trackedElement || !
|
|
669
|
+
if (!trackedElement || !call || trackType === 'none')
|
|
671
670
|
return;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
unobserve();
|
|
675
|
-
};
|
|
676
|
-
}, [trackedElement, manager, call, sessionId, trackType]);
|
|
671
|
+
return call.trackElementVisibility(trackedElement, sessionId, trackType);
|
|
672
|
+
}, [trackedElement, call, sessionId, trackType]);
|
|
677
673
|
};
|
|
678
674
|
|
|
679
675
|
const Avatar = ({ imageSrc, name, style, className, ...rest }) => {
|
|
@@ -1205,7 +1201,7 @@ const SpeakerTest = (props) => {
|
|
|
1205
1201
|
const audioElementRef = useRef(null);
|
|
1206
1202
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
1207
1203
|
const { t } = useI18n();
|
|
1208
|
-
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.
|
|
1204
|
+
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.37.1"}/assets/piano.mp3`, } = props;
|
|
1209
1205
|
// Update audio output device when selection changes
|
|
1210
1206
|
useEffect(() => {
|
|
1211
1207
|
const audio = audioElementRef.current;
|
|
@@ -1314,25 +1310,31 @@ forwardRef(function ToggleDeviceSettingsMenuButton({ menuShown }, ref) {
|
|
|
1314
1310
|
const ToggleAudioPreviewButton = (props) => {
|
|
1315
1311
|
const { caption, Menu = DeviceSelectorAudioInput, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
|
|
1316
1312
|
const { t } = useI18n();
|
|
1317
|
-
const { useMicrophoneState } = useCallStateHooks();
|
|
1313
|
+
const { useMicrophoneState, useLocalParticipant } = useCallStateHooks();
|
|
1318
1314
|
const { microphone, hasBrowserPermission, isPromptingPermission, optionsAwareIsMute, isTogglePending, } = useMicrophoneState({ optimisticUpdates });
|
|
1315
|
+
const localParticipant = useLocalParticipant();
|
|
1316
|
+
const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(SfuModels.TrackType.AUDIO);
|
|
1319
1317
|
const [tooltipDisabled, setTooltipDisabled] = useState(false);
|
|
1320
1318
|
const handleClick = createCallControlHandler(props, () => microphone.toggle());
|
|
1321
1319
|
return (jsx(WithTooltip, { title: !hasBrowserPermission
|
|
1322
1320
|
? t('Check your browser audio permissions')
|
|
1323
|
-
:
|
|
1321
|
+
: isSystemMuted
|
|
1322
|
+
? t('Microphone is paused by your system')
|
|
1323
|
+
: (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
|
|
1324
1324
|
? 'preview-audio-unmute-button'
|
|
1325
1325
|
: 'preview-audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
|
|
1326
1326
|
setTooltipDisabled(shown);
|
|
1327
1327
|
onMenuToggle?.(shown);
|
|
1328
|
-
}, children: [jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
|
|
1328
|
+
}, children: [jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }));
|
|
1329
1329
|
};
|
|
1330
1330
|
const ToggleAudioPublishingButton = (props) => {
|
|
1331
1331
|
const { t } = useI18n();
|
|
1332
1332
|
const { caption, Menu = jsx(DeviceSelectorAudioInput, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
|
|
1333
1333
|
const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(OwnCapability.SEND_AUDIO);
|
|
1334
|
-
const { useMicrophoneState } = useCallStateHooks();
|
|
1334
|
+
const { useMicrophoneState, useLocalParticipant } = useCallStateHooks();
|
|
1335
1335
|
const { microphone, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useMicrophoneState({ optimisticUpdates });
|
|
1336
|
+
const localParticipant = useLocalParticipant();
|
|
1337
|
+
const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(SfuModels.TrackType.AUDIO);
|
|
1336
1338
|
const [tooltipDisabled, setTooltipDisabled] = useState(false);
|
|
1337
1339
|
const handleClick = createCallControlHandler(props, async () => {
|
|
1338
1340
|
if (!hasPermission) {
|
|
@@ -1346,37 +1348,45 @@ const ToggleAudioPublishingButton = (props) => {
|
|
|
1346
1348
|
? t('You have no permission to share your audio')
|
|
1347
1349
|
: !hasBrowserPermission
|
|
1348
1350
|
? t('Check your browser mic permissions')
|
|
1349
|
-
:
|
|
1351
|
+
: isSystemMuted
|
|
1352
|
+
? t('Microphone is paused by your system')
|
|
1353
|
+
: (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
|
|
1350
1354
|
!hasPermission ||
|
|
1351
1355
|
// disable button while the toggle action is pending when not using optimistic updates
|
|
1352
1356
|
(!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'audio-unmute-button' : 'audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
|
|
1353
1357
|
setTooltipDisabled(shown);
|
|
1354
1358
|
onMenuToggle?.(shown);
|
|
1355
|
-
}, children: [jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }) }) }));
|
|
1359
|
+
}, children: [jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && hasPermission && (jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }) }) }));
|
|
1356
1360
|
};
|
|
1357
1361
|
|
|
1358
1362
|
const ToggleVideoPreviewButton = (props) => {
|
|
1359
1363
|
const { caption, Menu = DeviceSelectorVideo, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
|
|
1360
1364
|
const { t } = useI18n();
|
|
1361
|
-
const { useCameraState } = useCallStateHooks();
|
|
1365
|
+
const { useCameraState, useLocalParticipant } = useCallStateHooks();
|
|
1362
1366
|
const { camera, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useCameraState({ optimisticUpdates });
|
|
1367
|
+
const localParticipant = useLocalParticipant();
|
|
1368
|
+
const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(SfuModels.TrackType.VIDEO);
|
|
1363
1369
|
const [tooltipDisabled, setTooltipDisabled] = useState(false);
|
|
1364
1370
|
const handleClick = createCallControlHandler(props, () => camera.toggle());
|
|
1365
1371
|
return (jsx(WithTooltip, { title: !hasBrowserPermission
|
|
1366
1372
|
? t('Check your browser video permissions')
|
|
1367
|
-
:
|
|
1373
|
+
: isSystemMuted
|
|
1374
|
+
? t('Camera is paused by your system')
|
|
1375
|
+
: (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
|
|
1368
1376
|
? 'preview-video-unmute-button'
|
|
1369
1377
|
: 'preview-video-mute-button', onClick: handleClick, disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
|
|
1370
1378
|
setTooltipDisabled(shown);
|
|
1371
1379
|
onMenuToggle?.(shown);
|
|
1372
|
-
}, children: [jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
|
|
1380
|
+
}, children: [jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }));
|
|
1373
1381
|
};
|
|
1374
1382
|
const ToggleVideoPublishingButton = (props) => {
|
|
1375
1383
|
const { t } = useI18n();
|
|
1376
1384
|
const { caption, Menu = jsx(DeviceSelectorVideo, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
|
|
1377
1385
|
const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(OwnCapability.SEND_VIDEO);
|
|
1378
|
-
const { useCameraState, useCallSettings } = useCallStateHooks();
|
|
1386
|
+
const { useCameraState, useCallSettings, useLocalParticipant } = useCallStateHooks();
|
|
1379
1387
|
const { camera, optionsAwareIsMute, hasBrowserPermission, isPromptingPermission, isTogglePending, } = useCameraState({ optimisticUpdates });
|
|
1388
|
+
const localParticipant = useLocalParticipant();
|
|
1389
|
+
const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(SfuModels.TrackType.VIDEO);
|
|
1380
1390
|
const callSettings = useCallSettings();
|
|
1381
1391
|
const isPublishingVideoAllowed = callSettings?.video.enabled;
|
|
1382
1392
|
const [tooltipDisabled, setTooltipDisabled] = useState(false);
|
|
@@ -1394,7 +1404,9 @@ const ToggleVideoPublishingButton = (props) => {
|
|
|
1394
1404
|
? t('Check your browser video permissions')
|
|
1395
1405
|
: !isPublishingVideoAllowed
|
|
1396
1406
|
? t('Video publishing is disabled by the system')
|
|
1397
|
-
:
|
|
1407
|
+
: isSystemMuted
|
|
1408
|
+
? t('Camera is paused by your system')
|
|
1409
|
+
: caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
|
|
1398
1410
|
!hasPermission ||
|
|
1399
1411
|
!isPublishingVideoAllowed ||
|
|
1400
1412
|
(!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'video-unmute-button' : 'video-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
|
|
@@ -1402,7 +1414,10 @@ const ToggleVideoPublishingButton = (props) => {
|
|
|
1402
1414
|
onMenuToggle?.(shown);
|
|
1403
1415
|
}, children: [jsx(Icon, { icon: optionsAwareIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
|
|
1404
1416
|
!hasPermission ||
|
|
1405
|
-
!isPublishingVideoAllowed) && (jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))
|
|
1417
|
+
!isPublishingVideoAllowed) && (jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted &&
|
|
1418
|
+
hasBrowserPermission &&
|
|
1419
|
+
hasPermission &&
|
|
1420
|
+
isPublishingVideoAllowed && (jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }) }) }));
|
|
1406
1421
|
};
|
|
1407
1422
|
|
|
1408
1423
|
const EndCallMenu = (props) => {
|
|
@@ -1900,7 +1915,6 @@ const ParticipantView = forwardRef(function ParticipantView({ participant, track
|
|
|
1900
1915
|
const [trackedElement, setTrackedElement] = useState(null);
|
|
1901
1916
|
const [contextVideoElement, setContextVideoElement] = useState(null);
|
|
1902
1917
|
const [contextVideoPlaceholderElement, setContextVideoPlaceholderElement] = useState(null);
|
|
1903
|
-
// TODO: allow to pass custom ViewportTracker instance from props
|
|
1904
1918
|
useTrackElementVisibility({
|
|
1905
1919
|
sessionId,
|
|
1906
1920
|
trackedElement,
|
|
@@ -2031,8 +2045,10 @@ var en = {
|
|
|
2031
2045
|
Dismiss: Dismiss,
|
|
2032
2046
|
"Microphone on": "Microphone on",
|
|
2033
2047
|
"Microphone off": "Microphone off",
|
|
2048
|
+
"Microphone is paused by your system": "Microphone is paused by your system",
|
|
2034
2049
|
"Camera on": "Camera on",
|
|
2035
2050
|
"Camera off": "Camera off",
|
|
2051
|
+
"Camera is paused by your system": "Camera is paused by your system",
|
|
2036
2052
|
"No camera found": "No camera found",
|
|
2037
2053
|
"Video is disabled": "Video is disabled",
|
|
2038
2054
|
Pinned: Pinned,
|
|
@@ -2419,7 +2435,7 @@ const PaginatedGridLayout = (props) => {
|
|
|
2419
2435
|
if (!paginatedGridLayoutWrapperElement || !call)
|
|
2420
2436
|
return;
|
|
2421
2437
|
const cleanup = call.setViewport(paginatedGridLayoutWrapperElement);
|
|
2422
|
-
return () => cleanup();
|
|
2438
|
+
return () => cleanup?.();
|
|
2423
2439
|
}, [paginatedGridLayoutWrapperElement, call]);
|
|
2424
2440
|
// only used to render video elements
|
|
2425
2441
|
const participantGroups = useMemo(() => chunk(participants, groupSize), [participants, groupSize]);
|
|
@@ -2511,7 +2527,7 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
|
|
|
2511
2527
|
if (!participantsBarWrapperElement || !call)
|
|
2512
2528
|
return;
|
|
2513
2529
|
const cleanup = call.setViewport(participantsBarWrapperElement);
|
|
2514
|
-
return () => cleanup();
|
|
2530
|
+
return () => cleanup?.();
|
|
2515
2531
|
}, [participantsBarWrapperElement, call]);
|
|
2516
2532
|
const isOneOnOneCall = allParticipants.length === 2;
|
|
2517
2533
|
useSpeakerLayoutSortPreset(call, isOneOnOneCall);
|