@stream-io/video-react-sdk 1.36.1 → 1.37.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.
package/dist/index.cjs.js CHANGED
@@ -807,17 +807,13 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
807
807
  };
808
808
  Video$1.displayName = 'Video';
809
809
 
810
- const useTrackElementVisibility = ({ trackedElement, dynascaleManager: propsDynascaleManager, sessionId, trackType, }) => {
810
+ const useTrackElementVisibility = ({ trackedElement, sessionId, trackType, }) => {
811
811
  const call = videoReactBindings.useCall();
812
- const manager = propsDynascaleManager ?? call?.dynascaleManager;
813
812
  react.useEffect(() => {
814
- if (!trackedElement || !manager || !call || trackType === 'none')
813
+ if (!trackedElement || !call || trackType === 'none')
815
814
  return;
816
- const unobserve = manager.trackElementVisibility(trackedElement, sessionId, trackType);
817
- return () => {
818
- unobserve();
819
- };
820
- }, [trackedElement, manager, call, sessionId, trackType]);
815
+ return call.trackElementVisibility(trackedElement, sessionId, trackType);
816
+ }, [trackedElement, call, sessionId, trackType]);
821
817
  };
822
818
 
823
819
  const Avatar = ({ imageSrc, name, style, className, ...rest }) => {
@@ -1403,7 +1399,7 @@ const SpeakerTest = (props) => {
1403
1399
  const audioElementRef = react.useRef(null);
1404
1400
  const [isPlaying, setIsPlaying] = react.useState(false);
1405
1401
  const { t } = videoReactBindings.useI18n();
1406
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.36.1"}/assets/piano.mp3`, } = props;
1402
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.37.0"}/assets/piano.mp3`, } = props;
1407
1403
  // Update audio output device when selection changes
1408
1404
  react.useEffect(() => {
1409
1405
  const audio = audioElementRef.current;
@@ -1519,25 +1515,31 @@ const ToggleDeviceSettingsMenuButton = react.forwardRef(function ToggleDeviceSet
1519
1515
  const ToggleAudioPreviewButton = (props) => {
1520
1516
  const { caption, Menu = DeviceSelectorAudioInput, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1521
1517
  const { t } = videoReactBindings.useI18n();
1522
- const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
1518
+ const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1523
1519
  const { microphone, hasBrowserPermission, isPromptingPermission, optionsAwareIsMute, isTogglePending, } = useMicrophoneState({ optimisticUpdates });
1520
+ const localParticipant = useLocalParticipant();
1521
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.AUDIO);
1524
1522
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1525
1523
  const handleClick = createCallControlHandler(props, () => microphone.toggle());
1526
1524
  return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1527
1525
  ? t('Check your browser audio permissions')
1528
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1526
+ : isSystemMuted
1527
+ ? t('Microphone is paused by your system')
1528
+ : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute
1529
1529
  ? 'preview-audio-unmute-button'
1530
1530
  : 'preview-audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1531
1531
  setTooltipDisabled(shown);
1532
1532
  onMenuToggle?.(shown);
1533
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
1533
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }));
1534
1534
  };
1535
1535
  const ToggleAudioPublishingButton = (props) => {
1536
1536
  const { t } = videoReactBindings.useI18n();
1537
1537
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorAudioInput, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1538
1538
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.OwnCapability.SEND_AUDIO);
1539
- const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
1539
+ const { useMicrophoneState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1540
1540
  const { microphone, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useMicrophoneState({ optimisticUpdates });
1541
+ const localParticipant = useLocalParticipant();
1542
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.AUDIO);
1541
1543
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1542
1544
  const handleClick = createCallControlHandler(props, async () => {
1543
1545
  if (!hasPermission) {
@@ -1551,37 +1553,45 @@ const ToggleAudioPublishingButton = (props) => {
1551
1553
  ? t('You have no permission to share your audio')
1552
1554
  : !hasBrowserPermission
1553
1555
  ? t('Check your browser mic permissions')
1554
- : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1556
+ : isSystemMuted
1557
+ ? t('Microphone is paused by your system')
1558
+ : (caption ?? t('Mic')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1555
1559
  !hasPermission ||
1556
1560
  // disable button while the toggle action is pending when not using optimistic updates
1557
1561
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'audio-unmute-button' : 'audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1558
1562
  setTooltipDisabled(shown);
1559
1563
  onMenuToggle?.(shown);
1560
- }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }) }) }));
1564
+ }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && hasPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Microphone is paused by your system'), children: "!" }))] }) }) }) }));
1561
1565
  };
1562
1566
 
1563
1567
  const ToggleVideoPreviewButton = (props) => {
1564
1568
  const { caption, Menu = DeviceSelectorVideo, menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1565
1569
  const { t } = videoReactBindings.useI18n();
1566
- const { useCameraState } = videoReactBindings.useCallStateHooks();
1570
+ const { useCameraState, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1567
1571
  const { camera, hasBrowserPermission, isPromptingPermission, isTogglePending, optionsAwareIsMute, } = useCameraState({ optimisticUpdates });
1572
+ const localParticipant = useLocalParticipant();
1573
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.VIDEO);
1568
1574
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
1569
1575
  const handleClick = createCallControlHandler(props, () => camera.toggle());
1570
1576
  return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
1571
1577
  ? t('Check your browser video permissions')
1572
- : (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
1578
+ : isSystemMuted
1579
+ ? t('Camera is paused by your system')
1580
+ : (caption ?? t('Video')), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optionsAwareIsMute
1573
1581
  ? 'preview-video-unmute-button'
1574
1582
  : 'preview-video-mute-button', onClick: handleClick, disabled: !hasBrowserPermission || (!optimisticUpdates && isTogglePending), Menu: Menu, menuPlacement: menuPlacement, ...restCompositeButtonProps, onMenuToggle: (shown) => {
1575
1583
  setTooltipDisabled(shown);
1576
1584
  onMenuToggle?.(shown);
1577
- }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }));
1585
+ }, children: [jsxRuntime.jsx(Icon, { icon: !optionsAwareIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted && hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }));
1578
1586
  };
1579
1587
  const ToggleVideoPublishingButton = (props) => {
1580
1588
  const { t } = videoReactBindings.useI18n();
1581
1589
  const { caption, Menu = jsxRuntime.jsx(DeviceSelectorVideo, { visualType: "list" }), menuPlacement = 'top', onMenuToggle, optimisticUpdates, ...restCompositeButtonProps } = props;
1582
1590
  const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.OwnCapability.SEND_VIDEO);
1583
- const { useCameraState, useCallSettings } = videoReactBindings.useCallStateHooks();
1591
+ const { useCameraState, useCallSettings, useLocalParticipant } = videoReactBindings.useCallStateHooks();
1584
1592
  const { camera, optionsAwareIsMute, hasBrowserPermission, isPromptingPermission, isTogglePending, } = useCameraState({ optimisticUpdates });
1593
+ const localParticipant = useLocalParticipant();
1594
+ const isSystemMuted = !!localParticipant?.interruptedTracks?.includes(videoClient.SfuModels.TrackType.VIDEO);
1585
1595
  const callSettings = useCallSettings();
1586
1596
  const isPublishingVideoAllowed = callSettings?.video.enabled;
1587
1597
  const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
@@ -1599,7 +1609,9 @@ const ToggleVideoPublishingButton = (props) => {
1599
1609
  ? t('Check your browser video permissions')
1600
1610
  : !isPublishingVideoAllowed
1601
1611
  ? t('Video publishing is disabled by the system')
1602
- : caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1612
+ : isSystemMuted
1613
+ ? t('Camera is paused by your system')
1614
+ : caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optionsAwareIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
1603
1615
  !hasPermission ||
1604
1616
  !isPublishingVideoAllowed ||
1605
1617
  (!optimisticUpdates && isTogglePending), "data-testid": optionsAwareIsMute ? 'video-unmute-button' : 'video-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, onMenuToggle: (shown) => {
@@ -1607,7 +1619,10 @@ const ToggleVideoPublishingButton = (props) => {
1607
1619
  onMenuToggle?.(shown);
1608
1620
  }, children: [jsxRuntime.jsx(Icon, { icon: optionsAwareIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
1609
1621
  !hasPermission ||
1610
- !isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" }))] }) }) }) }));
1622
+ !isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" })), isPromptingPermission && (jsxRuntime.jsx("span", { className: "str-video__pending-permission", title: t('Waiting for permission'), children: "?" })), isSystemMuted &&
1623
+ hasBrowserPermission &&
1624
+ hasPermission &&
1625
+ isPublishingVideoAllowed && (jsxRuntime.jsx("span", { className: "str-video__system-muted", title: t('Camera is paused by your system'), children: "!" }))] }) }) }) }));
1611
1626
  };
1612
1627
 
1613
1628
  const EndCallMenu = (props) => {
@@ -2398,7 +2413,6 @@ const ParticipantView = react.forwardRef(function ParticipantView({ participant,
2398
2413
  const [trackedElement, setTrackedElement] = react.useState(null);
2399
2414
  const [contextVideoElement, setContextVideoElement] = react.useState(null);
2400
2415
  const [contextVideoPlaceholderElement, setContextVideoPlaceholderElement] = react.useState(null);
2401
- // TODO: allow to pass custom ViewportTracker instance from props
2402
2416
  useTrackElementVisibility({
2403
2417
  sessionId,
2404
2418
  trackedElement,
@@ -2529,8 +2543,10 @@ var en = {
2529
2543
  Dismiss: Dismiss,
2530
2544
  "Microphone on": "Microphone on",
2531
2545
  "Microphone off": "Microphone off",
2546
+ "Microphone is paused by your system": "Microphone is paused by your system",
2532
2547
  "Camera on": "Camera on",
2533
2548
  "Camera off": "Camera off",
2549
+ "Camera is paused by your system": "Camera is paused by your system",
2534
2550
  "No camera found": "No camera found",
2535
2551
  "Video is disabled": "Video is disabled",
2536
2552
  Pinned: Pinned,
@@ -2937,7 +2953,7 @@ const PaginatedGridLayout = (props) => {
2937
2953
  if (!paginatedGridLayoutWrapperElement || !call)
2938
2954
  return;
2939
2955
  const cleanup = call.setViewport(paginatedGridLayoutWrapperElement);
2940
- return () => cleanup();
2956
+ return () => cleanup?.();
2941
2957
  }, [paginatedGridLayoutWrapperElement, call]);
2942
2958
  // only used to render video elements
2943
2959
  const participantGroups = react.useMemo(() => chunk(participants, groupSize), [participants, groupSize]);
@@ -3029,7 +3045,7 @@ const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, Par
3029
3045
  if (!participantsBarWrapperElement || !call)
3030
3046
  return;
3031
3047
  const cleanup = call.setViewport(participantsBarWrapperElement);
3032
- return () => cleanup();
3048
+ return () => cleanup?.();
3033
3049
  }, [participantsBarWrapperElement, call]);
3034
3050
  const isOneOnOneCall = allParticipants.length === 2;
3035
3051
  useSpeakerLayoutSortPreset(call, isOneOnOneCall);
@@ -3236,7 +3252,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
3236
3252
  return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
3237
3253
  };
3238
3254
 
3239
- const [major, minor, patch] = ("1.36.1").split('.');
3255
+ const [major, minor, patch] = ("1.37.0").split('.');
3240
3256
  videoClient.setSdkInfo({
3241
3257
  type: videoClient.SfuModels.SdkType.REACT,
3242
3258
  major,