@stream-io/video-react-sdk 1.0.7 → 1.0.9
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 +16 -0
- package/dist/css/styles.css +3 -3
- package/dist/index.cjs.js +139 -87
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +140 -88
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/Button/CompositeButton.d.ts +1 -0
- package/dist/src/components/CallControls/CancelCallButton.d.ts +2 -1
- package/dist/src/components/CallControls/ScreenShareButton.d.ts +3 -2
- package/dist/src/components/CallControls/ToggleAudioButton.d.ts +3 -2
- package/dist/src/components/CallControls/ToggleVideoButton.d.ts +3 -2
- package/dist/src/components/Menu/MenuToggle.d.ts +2 -1
- package/dist/src/components/Tooltip/WithTooltip.d.ts +4 -2
- package/dist/src/utilities/callControlHandler.d.ts +16 -0
- package/package.json +4 -4
- package/src/components/Button/CompositeButton.tsx +3 -0
- package/src/components/CallControls/CallControls.tsx +3 -3
- package/src/components/CallControls/CancelCallButton.tsx +12 -8
- package/src/components/CallControls/ReactionsButton.tsx +14 -9
- package/src/components/CallControls/RecordCallButton.tsx +21 -15
- package/src/components/CallControls/ScreenShareButton.tsx +34 -26
- package/src/components/CallControls/ToggleAudioButton.tsx +84 -56
- package/src/components/CallControls/ToggleVideoButton.tsx +87 -59
- package/src/components/DeviceSettings/DeviceSelector.tsx +4 -0
- package/src/components/Menu/MenuToggle.tsx +9 -0
- package/src/components/Tooltip/WithTooltip.tsx +7 -2
- package/src/utilities/callControlHandler.ts +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [1.0.9](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.0.8...@stream-io/video-react-sdk-1.0.9) (2024-05-21)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `@stream-io/video-client` updated to version `1.0.7`
|
|
10
|
+
* `@stream-io/video-react-bindings` updated to version `0.4.33`
|
|
11
|
+
### [1.0.8](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.0.7...@stream-io/video-react-sdk-1.0.8) (2024-05-17)
|
|
12
|
+
|
|
13
|
+
### Dependency Updates
|
|
14
|
+
|
|
15
|
+
* `@stream-io/video-styling` updated to version `1.0.3`
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* popup-related UI updates ([#1356](https://github.com/GetStream/stream-video-js/issues/1356)) ([a1a3238](https://github.com/GetStream/stream-video-js/commit/a1a3238370b1ed5b7877f744bebea9f51a843256))
|
|
20
|
+
|
|
5
21
|
### [1.0.7](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-1.0.6...@stream-io/video-react-sdk-1.0.7) (2024-05-16)
|
|
6
22
|
|
|
7
23
|
### Dependency Updates
|
package/dist/css/styles.css
CHANGED
|
@@ -2388,7 +2388,7 @@
|
|
|
2388
2388
|
display: flex;
|
|
2389
2389
|
justify-content: center;
|
|
2390
2390
|
padding: 0.5rem 1rem;
|
|
2391
|
-
z-index:
|
|
2391
|
+
z-index: 4;
|
|
2392
2392
|
max-width: 250px;
|
|
2393
2393
|
width: max-content;
|
|
2394
2394
|
white-space: initial;
|
|
@@ -2397,7 +2397,7 @@
|
|
|
2397
2397
|
|
|
2398
2398
|
.str-video {
|
|
2399
2399
|
/* The border radius used for the borders of the component */
|
|
2400
|
-
--str-video__tooltip--border-radius: var(--str-video__border-radius-
|
|
2400
|
+
--str-video__tooltip--border-radius: var(--str-video__border-radius-xs);
|
|
2401
2401
|
/* The text/icon color of the component */
|
|
2402
2402
|
--str-video__tooltip--color: var(--str-video__text-color3);
|
|
2403
2403
|
/* The background color of the component */
|
|
@@ -2411,7 +2411,7 @@
|
|
|
2411
2411
|
/* Right (left in RTL layout) border of the component */
|
|
2412
2412
|
--str-video__tooltip--border-inline-end: none;
|
|
2413
2413
|
/* Box shadow applied to the component */
|
|
2414
|
-
--str-video__tooltip--box-shadow:
|
|
2414
|
+
--str-video__tooltip--box-shadow: none;
|
|
2415
2415
|
}
|
|
2416
2416
|
|
|
2417
2417
|
.str-video__tooltip {
|
package/dist/index.cjs.js
CHANGED
|
@@ -516,8 +516,10 @@ const MenuPortal = ({ children, refs, }) => {
|
|
|
516
516
|
const portalId = react.useMemo(() => `str-video-portal-${Math.random().toString(36).substring(2, 9)}`, []);
|
|
517
517
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { id: portalId, className: "str-video__portal" }), jsxRuntime.jsx(react$1.FloatingOverlay, { children: jsxRuntime.jsx(react$1.FloatingPortal, { id: portalId, children: jsxRuntime.jsx("div", { className: "str-video__portal-content", ref: refs.setFloating, children: children }) }) })] }));
|
|
518
518
|
};
|
|
519
|
-
const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolute', offset, visualType = exports.MenuVisualType.MENU, children, }) => {
|
|
519
|
+
const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolute', offset, visualType = exports.MenuVisualType.MENU, children, onToggle, }) => {
|
|
520
520
|
const [menuShown, setMenuShown] = react.useState(false);
|
|
521
|
+
const toggleHandler = react.useRef(onToggle);
|
|
522
|
+
toggleHandler.current = onToggle;
|
|
521
523
|
const { floating, domReference, refs, x, y } = useFloatingUIPreset({
|
|
522
524
|
placement,
|
|
523
525
|
strategy,
|
|
@@ -527,9 +529,11 @@ const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolut
|
|
|
527
529
|
const handleClick = (event) => {
|
|
528
530
|
if (!floating && domReference?.contains(event.target)) {
|
|
529
531
|
setMenuShown(true);
|
|
532
|
+
toggleHandler.current?.(true);
|
|
530
533
|
}
|
|
531
534
|
else if (floating && !floating?.contains(event.target)) {
|
|
532
535
|
setMenuShown(false);
|
|
536
|
+
toggleHandler.current?.(false);
|
|
533
537
|
}
|
|
534
538
|
};
|
|
535
539
|
const handleKeyDown = (event) => {
|
|
@@ -537,6 +541,7 @@ const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolut
|
|
|
537
541
|
!event.altKey &&
|
|
538
542
|
!event.ctrlKey) {
|
|
539
543
|
setMenuShown(false);
|
|
544
|
+
toggleHandler.current?.(false);
|
|
540
545
|
}
|
|
541
546
|
};
|
|
542
547
|
document?.addEventListener('click', handleClick, { capture: true });
|
|
@@ -551,7 +556,7 @@ const MenuToggle = ({ ToggleButton, placement = 'top-start', strategy = 'absolut
|
|
|
551
556
|
top: y ?? 0,
|
|
552
557
|
left: x ?? 0,
|
|
553
558
|
overflowY: 'auto',
|
|
554
|
-
}, children: children })) : null })), jsxRuntime.jsx(ToggleButton, { menuShown: menuShown, ref: refs.setReference })] }));
|
|
559
|
+
}, role: "menu", children: children })) : null })), jsxRuntime.jsx(ToggleButton, { menuShown: menuShown, ref: refs.setReference })] }));
|
|
555
560
|
};
|
|
556
561
|
|
|
557
562
|
const GenericMenu = ({ children, onItemClick, }) => {
|
|
@@ -600,7 +605,7 @@ const applyElementToRef = (ref, element) => {
|
|
|
600
605
|
ref.current = element;
|
|
601
606
|
};
|
|
602
607
|
|
|
603
|
-
const CompositeButton = react.forwardRef(function CompositeButton({ caption, children, className, active, Menu, menuPlacement, menuOffset, title, ToggleMenuButton = DefaultToggleMenuButton, variant, onClick, ...restButtonProps }, ref) {
|
|
608
|
+
const CompositeButton = react.forwardRef(function CompositeButton({ caption, children, className, active, Menu, menuPlacement, menuOffset, title, ToggleMenuButton = DefaultToggleMenuButton, variant, onClick, onMenuToggle, ...restButtonProps }, ref) {
|
|
604
609
|
return (jsxRuntime.jsxs("div", { className: clsx('str-video__composite-button', className, {
|
|
605
610
|
'str-video__composite-button--caption': caption,
|
|
606
611
|
'str-video__composite-button--menu': Menu,
|
|
@@ -611,7 +616,7 @@ const CompositeButton = react.forwardRef(function CompositeButton({ caption, chi
|
|
|
611
616
|
}), children: [jsxRuntime.jsx("button", { type: "button", className: "str-video__composite-button__button", onClick: (e) => {
|
|
612
617
|
e.preventDefault();
|
|
613
618
|
onClick?.(e);
|
|
614
|
-
}, ...restButtonProps, children: children }), Menu && (jsxRuntime.jsx(MenuToggle, { offset: menuOffset, placement: menuPlacement, ToggleButton: ToggleMenuButton, children: isComponentType(Menu) ? jsxRuntime.jsx(Menu, {}) : Menu }))] }), caption && (jsxRuntime.jsx("div", { className: "str-video__composite-button__caption", children: caption }))] }));
|
|
619
|
+
}, ...restButtonProps, children: children }), Menu && (jsxRuntime.jsx(MenuToggle, { offset: menuOffset, placement: menuPlacement, ToggleButton: ToggleMenuButton, onToggle: onMenuToggle, children: isComponentType(Menu) ? jsxRuntime.jsx(Menu, {}) : Menu }))] }), caption && (jsxRuntime.jsx("div", { className: "str-video__composite-button__caption", children: caption }))] }));
|
|
615
620
|
});
|
|
616
621
|
const DefaultToggleMenuButton = react.forwardRef(function DefaultToggleMenuButton({ menuShown }, ref) {
|
|
617
622
|
return (jsxRuntime.jsx(IconButton, { className: clsx('str-video__menu-toggle-button', {
|
|
@@ -710,6 +715,45 @@ const LoadingIndicator = ({ className, type = 'spinner', text, tooltip, }) => {
|
|
|
710
715
|
return (jsxRuntime.jsxs("div", { className: clsx('str-video__loading-indicator', className), title: tooltip, children: [jsxRuntime.jsx("div", { className: clsx('str-video__loading-indicator__icon', type) }), text && jsxRuntime.jsx("p", { className: "str-video__loading-indicator-text", children: text })] }));
|
|
711
716
|
};
|
|
712
717
|
|
|
718
|
+
const Tooltip = ({ children, referenceElement, tooltipClassName, tooltipPlacement = 'top', visible = false, }) => {
|
|
719
|
+
const { refs, x, y, strategy } = useFloatingUIPreset({
|
|
720
|
+
placement: tooltipPlacement,
|
|
721
|
+
strategy: 'absolute',
|
|
722
|
+
});
|
|
723
|
+
react.useEffect(() => {
|
|
724
|
+
refs.setReference(referenceElement);
|
|
725
|
+
}, [referenceElement, refs]);
|
|
726
|
+
if (!visible)
|
|
727
|
+
return null;
|
|
728
|
+
return (jsxRuntime.jsx("div", { className: clsx('str-video__tooltip', tooltipClassName), ref: refs.setFloating, style: {
|
|
729
|
+
position: strategy,
|
|
730
|
+
top: y ?? 0,
|
|
731
|
+
left: x ?? 0,
|
|
732
|
+
overflowY: 'auto',
|
|
733
|
+
}, children: children }));
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const useEnterLeaveHandlers = ({ onMouseEnter, onMouseLeave, } = {}) => {
|
|
737
|
+
const [tooltipVisible, setTooltipVisible] = react.useState(false);
|
|
738
|
+
const handleMouseEnter = react.useCallback((e) => {
|
|
739
|
+
setTooltipVisible(true);
|
|
740
|
+
onMouseEnter?.(e);
|
|
741
|
+
}, [onMouseEnter]);
|
|
742
|
+
const handleMouseLeave = react.useCallback((e) => {
|
|
743
|
+
setTooltipVisible(false);
|
|
744
|
+
onMouseLeave?.(e);
|
|
745
|
+
}, [onMouseLeave]);
|
|
746
|
+
return { handleMouseEnter, handleMouseLeave, tooltipVisible };
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
// todo: duplicate of CallParticipantList.tsx#MediaIndicator - refactor to a single component
|
|
750
|
+
const WithTooltip = ({ title, tooltipClassName, tooltipPlacement, tooltipDisabled, ...props }) => {
|
|
751
|
+
const { handleMouseEnter, handleMouseLeave, tooltipVisible } = useEnterLeaveHandlers();
|
|
752
|
+
const [tooltipAnchor, setTooltipAnchor] = react.useState(null);
|
|
753
|
+
const tooltipActuallyVisible = !tooltipDisabled && Boolean(title) && tooltipVisible;
|
|
754
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Tooltip, { referenceElement: tooltipAnchor, visible: tooltipActuallyVisible, tooltipClassName: tooltipClassName, tooltipPlacement: tooltipPlacement, children: title || '' }), jsxRuntime.jsx("div", { ref: setTooltipAnchor, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props })] }));
|
|
755
|
+
};
|
|
756
|
+
|
|
713
757
|
const RecordEndConfirmation = () => {
|
|
714
758
|
const { t } = videoReactBindings.useI18n();
|
|
715
759
|
const { toggleCallRecording, isAwaitingResponse } = useToggleCallRecording();
|
|
@@ -728,15 +772,18 @@ const RecordCallConfirmationButton = ({ caption, }) => {
|
|
|
728
772
|
videoClient.OwnCapability.STOP_RECORD_CALL,
|
|
729
773
|
], children: jsxRuntime.jsx(MenuToggle, { ToggleButton: ToggleEndRecordingMenuButton, visualType: exports.MenuVisualType.PORTAL, children: jsxRuntime.jsx(RecordEndConfirmation, {}) }) }));
|
|
730
774
|
}
|
|
775
|
+
const title = isAwaitingResponse
|
|
776
|
+
? t('Waiting for recording to start...')
|
|
777
|
+
: caption ?? t('Record call');
|
|
731
778
|
return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [
|
|
732
779
|
videoClient.OwnCapability.START_RECORD_CALL,
|
|
733
780
|
videoClient.OwnCapability.STOP_RECORD_CALL,
|
|
734
|
-
], children: jsxRuntime.jsx(
|
|
781
|
+
], children: jsxRuntime.jsx(WithTooltip, { title: title, children: jsxRuntime.jsx(CompositeButton, { active: isCallRecordingInProgress, caption: caption, variant: "secondary", "data-testid": "recording-start-button", onClick: isAwaitingResponse ? undefined : toggleCallRecording, children: isAwaitingResponse ? (jsxRuntime.jsx(LoadingIndicator, {})) : (jsxRuntime.jsx(Icon, { icon: "recording-off" })) }) }) }));
|
|
735
782
|
};
|
|
736
783
|
const RecordCallButton = ({ caption }) => {
|
|
737
784
|
const { t } = videoReactBindings.useI18n();
|
|
738
785
|
const { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress } = useToggleCallRecording();
|
|
739
|
-
let title = caption
|
|
786
|
+
let title = caption ?? t('Record call');
|
|
740
787
|
if (isAwaitingResponse) {
|
|
741
788
|
title = isCallRecordingInProgress
|
|
742
789
|
? t('Waiting for recording to stop...')
|
|
@@ -808,18 +855,48 @@ const ReactionsButton = ({ reactions = defaultReactions, }) => {
|
|
|
808
855
|
};
|
|
809
856
|
const ToggleReactionsMenuButton = react.forwardRef(function ToggleReactionsMenuButton({ menuShown }, ref) {
|
|
810
857
|
const { t } = videoReactBindings.useI18n();
|
|
811
|
-
return (jsxRuntime.jsx(CompositeButton, { ref: ref, active: menuShown, variant: "primary",
|
|
858
|
+
return (jsxRuntime.jsx(WithTooltip, { title: t('Reactions'), tooltipDisabled: menuShown, children: jsxRuntime.jsx(CompositeButton, { ref: ref, active: menuShown, variant: "primary", children: jsxRuntime.jsx(Icon, { icon: "reactions" }) }) }));
|
|
812
859
|
});
|
|
813
860
|
const DefaultReactionsMenu = ({ reactions, layout = 'horizontal', }) => {
|
|
814
861
|
const call = videoReactBindings.useCall();
|
|
862
|
+
const { close } = useMenuContext();
|
|
815
863
|
return (jsxRuntime.jsx("div", { className: clsx('str-video__reactions-menu', {
|
|
816
864
|
'str-video__reactions-menu--horizontal': layout === 'horizontal',
|
|
817
865
|
'str-video__reactions-menu--vertical': layout === 'vertical',
|
|
818
866
|
}), children: reactions.map((reaction) => (jsxRuntime.jsx("button", { type: "button", className: "str-video__reactions-menu__button", onClick: () => {
|
|
819
867
|
call?.sendReaction(reaction);
|
|
868
|
+
close?.();
|
|
820
869
|
}, children: reaction.emoji_code && defaultEmojiReactionMap[reaction.emoji_code] }, reaction.emoji_code))) }));
|
|
821
870
|
};
|
|
822
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Wraps an event handler, silencing and logging exceptions (excluding the NotAllowedError
|
|
874
|
+
* DOMException, which is a normal situation handled by the SDK)
|
|
875
|
+
*
|
|
876
|
+
* @param props component props, including the onError callback
|
|
877
|
+
* @param handler event handler to wrap
|
|
878
|
+
*/
|
|
879
|
+
const createCallControlHandler = (props, handler) => {
|
|
880
|
+
const logger = videoClient.getLogger(['react-sdk']);
|
|
881
|
+
return async () => {
|
|
882
|
+
try {
|
|
883
|
+
await handler();
|
|
884
|
+
}
|
|
885
|
+
catch (error) {
|
|
886
|
+
if (props.onError) {
|
|
887
|
+
props.onError(error);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (!isNotAllowedError(error)) {
|
|
891
|
+
logger('error', 'Call control handler failed', error);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
};
|
|
896
|
+
function isNotAllowedError(error) {
|
|
897
|
+
return error instanceof DOMException && error.name === 'NotAllowedError';
|
|
898
|
+
}
|
|
899
|
+
|
|
823
900
|
const ScreenShareButton = (props) => {
|
|
824
901
|
const { t } = videoReactBindings.useI18n();
|
|
825
902
|
const { caption } = props;
|
|
@@ -832,16 +909,17 @@ const ScreenShareButton = (props) => {
|
|
|
832
909
|
const amIScreenSharing = !optimisticIsMute;
|
|
833
910
|
const disableScreenShareButton = !amIScreenSharing &&
|
|
834
911
|
(isSomeoneScreenSharing || isScreenSharingAllowed === false);
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
912
|
+
const handleClick = createCallControlHandler(props, async () => {
|
|
913
|
+
if (!hasPermission) {
|
|
914
|
+
await requestPermission();
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
await screenShare.toggle();
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SCREENSHARE], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SCREENSHARE, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now share your screen.'), messageAwaitingApproval: t('Awaiting for an approval to share screen.'), messageRevoked: t('You can no longer share your screen.'), children: jsxRuntime.jsx(WithTooltip, { title: caption ?? t('Share screen'), children: jsxRuntime.jsx(CompositeButton, { active: isSomeoneScreenSharing || amIScreenSharing, caption: caption, variant: "primary", "data-testid": isSomeoneScreenSharing
|
|
921
|
+
? 'screen-share-stop-button'
|
|
922
|
+
: 'screen-share-start-button', disabled: disableScreenShareButton, onClick: handleClick, children: jsxRuntime.jsx(Icon, { icon: isSomeoneScreenSharing ? 'screen-share-on' : 'screen-share-off' }) }) }) }) }));
|
|
845
923
|
};
|
|
846
924
|
|
|
847
925
|
const SelectContext = react.createContext({});
|
|
@@ -923,6 +1001,7 @@ const DeviceSelectorOption = ({ disabled, id, label, onChange, name, selected, d
|
|
|
923
1001
|
};
|
|
924
1002
|
const DeviceSelectorList = (props) => {
|
|
925
1003
|
const { devices = [], selectedDeviceId: selectedDeviceFromProps, title, type, onChange, } = props;
|
|
1004
|
+
const { close } = useMenuContext();
|
|
926
1005
|
// sometimes the browser (Chrome) will report the system-default device
|
|
927
1006
|
// with an id of 'default'. In case when it doesn't, we'll select the first
|
|
928
1007
|
// available device.
|
|
@@ -934,6 +1013,7 @@ const DeviceSelectorList = (props) => {
|
|
|
934
1013
|
return (jsxRuntime.jsxs("div", { className: "str-video__device-settings__device-kind", children: [title && (jsxRuntime.jsx("div", { className: "str-video__device-settings__device-selector-title", children: title })), !devices.length ? (jsxRuntime.jsx(DeviceSelectorOption, { id: `${type}--default`, label: "Default", name: type, defaultChecked: true, value: "default" })) : (devices.map((device) => {
|
|
935
1014
|
return (jsxRuntime.jsx(DeviceSelectorOption, { id: `${type}--${device.deviceId}`, value: device.deviceId, label: device.label, onChange: (e) => {
|
|
936
1015
|
onChange?.(e.target.value);
|
|
1016
|
+
close?.();
|
|
937
1017
|
}, name: type, selected: device.deviceId === selectedDeviceId || devices.length === 1 }, device.deviceId));
|
|
938
1018
|
}))] }));
|
|
939
1019
|
};
|
|
@@ -1007,11 +1087,13 @@ const ToggleAudioPreviewButton = (props) => {
|
|
|
1007
1087
|
const { t } = videoReactBindings.useI18n();
|
|
1008
1088
|
const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
|
|
1009
1089
|
const { microphone, optimisticIsMute, hasBrowserPermission } = useMicrophoneState();
|
|
1010
|
-
|
|
1090
|
+
const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
|
|
1091
|
+
const handleClick = createCallControlHandler(props, () => microphone.toggle());
|
|
1092
|
+
return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
|
|
1011
1093
|
? t('Check your browser audio permissions')
|
|
1012
|
-
: caption
|
|
1013
|
-
|
|
1014
|
-
|
|
1094
|
+
: caption ?? t('Mic'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optimisticIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", disabled: !hasBrowserPermission, "data-testid": optimisticIsMute
|
|
1095
|
+
? 'preview-audio-unmute-button'
|
|
1096
|
+
: 'preview-audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, onMenuToggle: (shown) => setTooltipDisabled(shown), ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: !optimisticIsMute ? 'mic' : 'mic-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser audio permissions'), children: "!" }))] }) }));
|
|
1015
1097
|
};
|
|
1016
1098
|
const ToggleAudioPublishingButton = (props) => {
|
|
1017
1099
|
const { t } = videoReactBindings.useI18n();
|
|
@@ -1019,18 +1101,20 @@ const ToggleAudioPublishingButton = (props) => {
|
|
|
1019
1101
|
const { hasPermission, requestPermission, isAwaitingPermission } = useRequestPermission(videoClient.OwnCapability.SEND_AUDIO);
|
|
1020
1102
|
const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
|
|
1021
1103
|
const { microphone, optimisticIsMute, hasBrowserPermission } = useMicrophoneState();
|
|
1022
|
-
|
|
1104
|
+
const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
|
|
1105
|
+
const handleClick = createCallControlHandler(props, async () => {
|
|
1106
|
+
if (!hasPermission) {
|
|
1107
|
+
await requestPermission();
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
await microphone.toggle();
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_AUDIO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_AUDIO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now speak.'), messageAwaitingApproval: t('Awaiting for an approval to speak.'), messageRevoked: t('You can no longer speak.'), children: jsxRuntime.jsx(WithTooltip, { title: !hasPermission
|
|
1023
1114
|
? t('You have no permission to share your audio')
|
|
1024
1115
|
: !hasBrowserPermission
|
|
1025
1116
|
? t('Check your browser mic permissions')
|
|
1026
|
-
: caption
|
|
1027
|
-
if (!hasPermission) {
|
|
1028
|
-
await requestPermission();
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1031
|
-
await microphone.toggle();
|
|
1032
|
-
}
|
|
1033
|
-
}, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: optimisticIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" }))] }) }) }));
|
|
1117
|
+
: caption ?? t('Mic'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optimisticIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission || !hasPermission, "data-testid": optimisticIsMute ? 'audio-unmute-button' : 'audio-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, onMenuToggle: (shown) => setTooltipDisabled(shown), ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: optimisticIsMute ? 'mic-off' : 'mic' }), (!hasBrowserPermission || !hasPermission) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" }))] }) }) }) }));
|
|
1034
1118
|
};
|
|
1035
1119
|
|
|
1036
1120
|
const ToggleVideoPreviewButton = (props) => {
|
|
@@ -1038,11 +1122,13 @@ const ToggleVideoPreviewButton = (props) => {
|
|
|
1038
1122
|
const { t } = videoReactBindings.useI18n();
|
|
1039
1123
|
const { useCameraState } = videoReactBindings.useCallStateHooks();
|
|
1040
1124
|
const { camera, optimisticIsMute, hasBrowserPermission } = useCameraState();
|
|
1041
|
-
|
|
1125
|
+
const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
|
|
1126
|
+
const handleClick = createCallControlHandler(props, () => camera.toggle());
|
|
1127
|
+
return (jsxRuntime.jsx(WithTooltip, { title: !hasBrowserPermission
|
|
1042
1128
|
? t('Check your browser video permissions')
|
|
1043
|
-
: caption
|
|
1044
|
-
|
|
1045
|
-
|
|
1129
|
+
: caption ?? t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optimisticIsMute, caption: caption, className: clsx(!hasBrowserPermission && 'str-video__device-unavailable'), variant: "secondary", "data-testid": optimisticIsMute
|
|
1130
|
+
? 'preview-video-unmute-button'
|
|
1131
|
+
: 'preview-video-mute-button', onClick: handleClick, disabled: !hasBrowserPermission, Menu: Menu, menuPlacement: menuPlacement, onMenuToggle: (shown) => setTooltipDisabled(shown), ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: !optimisticIsMute ? 'camera' : 'camera-off' }), !hasBrowserPermission && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", title: t('Check your browser video permissions'), children: "!" }))] }) }));
|
|
1046
1132
|
};
|
|
1047
1133
|
const ToggleVideoPublishingButton = (props) => {
|
|
1048
1134
|
const { t } = videoReactBindings.useI18n();
|
|
@@ -1052,22 +1138,26 @@ const ToggleVideoPublishingButton = (props) => {
|
|
|
1052
1138
|
const { camera, optimisticIsMute, hasBrowserPermission } = useCameraState();
|
|
1053
1139
|
const callSettings = useCallSettings();
|
|
1054
1140
|
const isPublishingVideoAllowed = callSettings?.video.enabled;
|
|
1055
|
-
|
|
1141
|
+
const [tooltipDisabled, setTooltipDisabled] = react.useState(false);
|
|
1142
|
+
const handleClick = createCallControlHandler(props, async () => {
|
|
1143
|
+
if (!hasPermission) {
|
|
1144
|
+
await requestPermission();
|
|
1145
|
+
}
|
|
1146
|
+
else {
|
|
1147
|
+
await camera.toggle();
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
return (jsxRuntime.jsx(videoReactBindings.Restricted, { requiredGrants: [videoClient.OwnCapability.SEND_VIDEO], children: jsxRuntime.jsx(PermissionNotification, { permission: videoClient.OwnCapability.SEND_VIDEO, isAwaitingApproval: isAwaitingPermission, messageApproved: t('You can now share your video.'), messageAwaitingApproval: t('Awaiting for an approval to share your video.'), messageRevoked: t('You can no longer share your video.'), children: jsxRuntime.jsx(WithTooltip, { title: !hasPermission
|
|
1056
1151
|
? t('You have no permission to share your video')
|
|
1057
1152
|
: !hasBrowserPermission
|
|
1058
1153
|
? t('Check your browser video permissions')
|
|
1059
1154
|
: !isPublishingVideoAllowed
|
|
1060
1155
|
? t('Video publishing is disabled by the system')
|
|
1061
|
-
: caption || t('Video'),
|
|
1062
|
-
if (!hasPermission) {
|
|
1063
|
-
await requestPermission();
|
|
1064
|
-
}
|
|
1065
|
-
else {
|
|
1066
|
-
await camera.toggle();
|
|
1067
|
-
}
|
|
1068
|
-
}, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: optimisticIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
|
|
1156
|
+
: caption || t('Video'), tooltipDisabled: tooltipDisabled, children: jsxRuntime.jsxs(CompositeButton, { active: optimisticIsMute, caption: caption, variant: "secondary", disabled: !hasBrowserPermission ||
|
|
1069
1157
|
!hasPermission ||
|
|
1070
|
-
!isPublishingVideoAllowed)
|
|
1158
|
+
!isPublishingVideoAllowed, "data-testid": optimisticIsMute ? 'video-unmute-button' : 'video-mute-button', onClick: handleClick, Menu: Menu, menuPlacement: menuPlacement, menuOffset: 16, onMenuToggle: (shown) => setTooltipDisabled(shown), ...restCompositeButtonProps, children: [jsxRuntime.jsx(Icon, { icon: optimisticIsMute ? 'camera-off' : 'camera' }), (!hasBrowserPermission ||
|
|
1159
|
+
!hasPermission ||
|
|
1160
|
+
!isPublishingVideoAllowed) && (jsxRuntime.jsx("span", { className: "str-video__no-media-permission", children: "!" }))] }) }) }) }));
|
|
1071
1161
|
};
|
|
1072
1162
|
|
|
1073
1163
|
const EndCallMenu = (props) => {
|
|
@@ -1077,7 +1167,7 @@ const EndCallMenu = (props) => {
|
|
|
1077
1167
|
};
|
|
1078
1168
|
const CancelCallToggleMenuButton = react.forwardRef(function CancelCallToggleMenuButton(props, ref) {
|
|
1079
1169
|
const { t } = videoReactBindings.useI18n();
|
|
1080
|
-
return (jsxRuntime.jsx(IconButton, { icon: "call-end", variant: "danger",
|
|
1170
|
+
return (jsxRuntime.jsx(WithTooltip, { title: t('Leave call'), children: jsxRuntime.jsx(IconButton, { icon: "call-end", variant: "danger", "data-testid": "leave-call-button", ref: ref }) }));
|
|
1081
1171
|
});
|
|
1082
1172
|
const CancelCallConfirmButton = ({ onClick, onLeave, }) => {
|
|
1083
1173
|
const call = videoReactBindings.useCall();
|
|
@@ -1101,7 +1191,7 @@ const CancelCallConfirmButton = ({ onClick, onLeave, }) => {
|
|
|
1101
1191
|
}, [onClick, onLeave, call]);
|
|
1102
1192
|
return (jsxRuntime.jsx(MenuToggle, { placement: "top-start", ToggleButton: CancelCallToggleMenuButton, children: jsxRuntime.jsx(EndCallMenu, { onEnd: handleEndCall, onLeave: handleLeave }) }));
|
|
1103
1193
|
};
|
|
1104
|
-
const CancelCallButton = ({ disabled, onClick, onLeave, }) => {
|
|
1194
|
+
const CancelCallButton = ({ disabled, caption, onClick, onLeave, }) => {
|
|
1105
1195
|
const call = videoReactBindings.useCall();
|
|
1106
1196
|
const { t } = videoReactBindings.useI18n();
|
|
1107
1197
|
const handleClick = react.useCallback(async (e) => {
|
|
@@ -1113,10 +1203,10 @@ const CancelCallButton = ({ disabled, onClick, onLeave, }) => {
|
|
|
1113
1203
|
onLeave?.();
|
|
1114
1204
|
}
|
|
1115
1205
|
}, [onClick, onLeave, call]);
|
|
1116
|
-
return (jsxRuntime.jsx(IconButton, { disabled: disabled, icon: "call-end", variant: "danger", title: t('Leave call'), "data-testid": "cancel-call-button", onClick: handleClick }));
|
|
1206
|
+
return (jsxRuntime.jsx(IconButton, { disabled: disabled, icon: "call-end", variant: "danger", title: caption ?? t('Leave call'), "data-testid": "cancel-call-button", onClick: handleClick }));
|
|
1117
1207
|
};
|
|
1118
1208
|
|
|
1119
|
-
const CallControls = ({ onLeave }) => (jsxRuntime.jsxs("div", { className: "str-video__call-controls", children: [jsxRuntime.jsx(
|
|
1209
|
+
const CallControls = ({ onLeave }) => (jsxRuntime.jsxs("div", { className: "str-video__call-controls", children: [jsxRuntime.jsx(SpeakingWhileMutedNotification, { children: jsxRuntime.jsx(ToggleAudioPublishingButton, {}) }), jsxRuntime.jsx(ToggleVideoPublishingButton, {}), jsxRuntime.jsx(ReactionsButton, {}), jsxRuntime.jsx(ScreenShareButton, {}), jsxRuntime.jsx(RecordCallButton, {}), jsxRuntime.jsx(CancelCallButton, { onLeave: onLeave })] }));
|
|
1120
1210
|
|
|
1121
1211
|
chart_js.Chart.register(chart_js.CategoryScale, chart_js.LinearScale, chart_js.LineElement, chart_js.PointElement);
|
|
1122
1212
|
const CallStatsLatencyChart = (props) => {
|
|
@@ -1333,44 +1423,6 @@ const CallParticipantListHeader = ({ onClose, }) => {
|
|
|
1333
1423
|
return (jsxRuntime.jsxs("div", { className: "str-video__participant-list-header", children: [jsxRuntime.jsxs("div", { className: "str-video__participant-list-header__title", children: [t('Participants'), ' ', jsxRuntime.jsxs("span", { className: "str-video__participant-list-header__title-count", children: ["[", participants.length, "]"] }), anonymousParticipantCount > 0 && (jsxRuntime.jsx("span", { className: "str-video__participant-list-header__title-anonymous", children: t('Anonymous', { count: anonymousParticipantCount }) }))] }), jsxRuntime.jsx(IconButton, { onClick: onClose, className: "str-video__participant-list-header__close-button", icon: "close" })] }));
|
|
1334
1424
|
};
|
|
1335
1425
|
|
|
1336
|
-
const Tooltip = ({ children, referenceElement, tooltipClassName, tooltipPlacement = 'top', visible = false, }) => {
|
|
1337
|
-
const { refs, x, y, strategy } = useFloatingUIPreset({
|
|
1338
|
-
placement: tooltipPlacement,
|
|
1339
|
-
strategy: 'absolute',
|
|
1340
|
-
});
|
|
1341
|
-
react.useEffect(() => {
|
|
1342
|
-
refs.setReference(referenceElement);
|
|
1343
|
-
}, [referenceElement, refs]);
|
|
1344
|
-
if (!visible)
|
|
1345
|
-
return null;
|
|
1346
|
-
return (jsxRuntime.jsx("div", { className: clsx('str-video__tooltip', tooltipClassName), ref: refs.setFloating, style: {
|
|
1347
|
-
position: strategy,
|
|
1348
|
-
top: y ?? 0,
|
|
1349
|
-
left: x ?? 0,
|
|
1350
|
-
overflowY: 'auto',
|
|
1351
|
-
}, children: children }));
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
|
-
const useEnterLeaveHandlers = ({ onMouseEnter, onMouseLeave, } = {}) => {
|
|
1355
|
-
const [tooltipVisible, setTooltipVisible] = react.useState(false);
|
|
1356
|
-
const handleMouseEnter = react.useCallback((e) => {
|
|
1357
|
-
setTooltipVisible(true);
|
|
1358
|
-
onMouseEnter?.(e);
|
|
1359
|
-
}, [onMouseEnter]);
|
|
1360
|
-
const handleMouseLeave = react.useCallback((e) => {
|
|
1361
|
-
setTooltipVisible(false);
|
|
1362
|
-
onMouseLeave?.(e);
|
|
1363
|
-
}, [onMouseLeave]);
|
|
1364
|
-
return { handleMouseEnter, handleMouseLeave, tooltipVisible };
|
|
1365
|
-
};
|
|
1366
|
-
|
|
1367
|
-
// todo: duplicate of CallParticipantList.tsx#MediaIndicator - refactor to a single component
|
|
1368
|
-
const WithTooltip = ({ title, tooltipClassName, tooltipPlacement, ...props }) => {
|
|
1369
|
-
const { handleMouseEnter, handleMouseLeave, tooltipVisible } = useEnterLeaveHandlers();
|
|
1370
|
-
const [tooltipAnchor, setTooltipAnchor] = react.useState(null);
|
|
1371
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Tooltip, { referenceElement: tooltipAnchor, visible: tooltipVisible, tooltipClassName: tooltipClassName, tooltipPlacement: tooltipPlacement, children: title || '' }), jsxRuntime.jsx("div", { ref: setTooltipAnchor, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props })] }));
|
|
1372
|
-
};
|
|
1373
|
-
|
|
1374
1426
|
const CallParticipantListingItem = ({ participant, DisplayName = DefaultDisplayName, }) => {
|
|
1375
1427
|
const isAudioOn = videoClient.hasAudio(participant);
|
|
1376
1428
|
const isVideoOn = videoClient.hasVideo(participant);
|
|
@@ -2515,7 +2567,7 @@ const VerticalScrollButtons = ({ scrollWrapper, }) => {
|
|
|
2515
2567
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [scrollPosition && scrollPosition !== 'top' && (jsxRuntime.jsx(IconButton, { onClick: scrollTopClickHandler, icon: "caret-up", className: "str-video__speaker-layout__participants-bar--button-top" })), scrollPosition && scrollPosition !== 'bottom' && (jsxRuntime.jsx(IconButton, { onClick: scrollBottomClickHandler, icon: "caret-down", className: "str-video__speaker-layout__participants-bar--button-bottom" }))] }));
|
|
2516
2568
|
};
|
|
2517
2569
|
|
|
2518
|
-
const [major, minor, patch] = ("1.0.
|
|
2570
|
+
const [major, minor, patch] = ("1.0.9" ).split('.');
|
|
2519
2571
|
videoClient.setSdkInfo({
|
|
2520
2572
|
type: videoClient.SfuModels.SdkType.REACT,
|
|
2521
2573
|
major,
|