[video-control-plugin-sdk] created extensible-are screenshare helper and refactored buttons rendering for screenshare
This commit is contained in:
parent
da62c1068e
commit
6f35d4d366
@ -0,0 +1,53 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
ExtensibleAreaComponentManagerProps, ExtensibleArea,
|
||||
ExtensibleAreaComponentManager,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../../components-data/plugin-context/context';
|
||||
|
||||
const ScreenshareHelperPluginStateContainer = ((
|
||||
props: ExtensibleAreaComponentManagerProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
extensibleAreaMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
screenshareHelperItems,
|
||||
setScreenshareHelperItems,
|
||||
] = useState<PluginSdk.ScreenshareHelperInterface[]>([]);
|
||||
|
||||
const {
|
||||
pluginsExtensibleAreasAggregatedState,
|
||||
setPluginsExtensibleAreasAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
extensibleAreaMap[uuid].screenshareHelperItems = screenshareHelperItems;
|
||||
|
||||
const aggregatedScreenshareHelperItems = ([] as PluginSdk.ScreenshareHelperInterface[]).concat(
|
||||
...Object.values(extensibleAreaMap)
|
||||
.map((extensibleArea: ExtensibleArea) => extensibleArea.screenshareHelperItems),
|
||||
);
|
||||
|
||||
setPluginsExtensibleAreasAggregatedState(
|
||||
{
|
||||
...pluginsExtensibleAreasAggregatedState,
|
||||
screenshareHelperItems: aggregatedScreenshareHelperItems,
|
||||
},
|
||||
);
|
||||
}, [screenshareHelperItems]);
|
||||
|
||||
pluginApi.setScreenshareHelperItems = (items: PluginSdk.ScreenshareHelperInterface[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.ScreenshareHelperInterface[];
|
||||
setScreenshareHelperItems(itemsWithId);
|
||||
return itemsWithId.map((i) => i.id);
|
||||
};
|
||||
return null;
|
||||
}) as ExtensibleAreaComponentManager;
|
||||
|
||||
export default ScreenshareHelperPluginStateContainer;
|
@ -19,6 +19,7 @@ import {
|
||||
} from './types';
|
||||
import FloatingWindowPluginStateContainer from './components/floating-window/manager';
|
||||
import GenericContentPluginStateContainer from './components/generic-content/manager';
|
||||
import ScreenshareHelperPluginStateContainer from './components/screenshare-helper/manager';
|
||||
|
||||
const extensibleAreaMap: ExtensibleAreaMap = {};
|
||||
|
||||
@ -36,6 +37,7 @@ const extensibleAreaComponentManagers: ExtensibleAreaComponentManager[] = [
|
||||
UserListItemAdditionalInformationPluginStateContainer,
|
||||
FloatingWindowPluginStateContainer,
|
||||
GenericContentPluginStateContainer,
|
||||
ScreenshareHelperPluginStateContainer,
|
||||
];
|
||||
|
||||
function generateItemWithId<T extends PluginProvidedUiItemDescriptor>(
|
||||
|
@ -15,6 +15,7 @@ export interface ExtensibleArea {
|
||||
actionsBarItems: PluginSdk.ActionsBarInterface[];
|
||||
presentationDropdownItems: PluginSdk.PresentationDropdownInterface[];
|
||||
navBarItems: PluginSdk.NavBarInterface[];
|
||||
screenshareHelperItems: PluginSdk.ScreenshareHelperInterface[];
|
||||
optionsDropdownItems: PluginSdk.OptionsDropdownInterface[];
|
||||
cameraSettingsDropdownItems: PluginSdk.CameraSettingsDropdownInterface[];
|
||||
userCameraDropdownItems: PluginSdk.UserCameraDropdownInterface[];
|
||||
|
@ -5,7 +5,9 @@ import { debounce } from '/imports/utils/debounce';
|
||||
import FullscreenButtonContainer from '/imports/ui/components/common/fullscreen-button/container';
|
||||
import SwitchButtonContainer from './switch-button/container';
|
||||
import Styled from './styles';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import VolumeSlider from '../external-video-player/volume-slider/component';
|
||||
import PluginButtonContainer from './plugin-button/container';
|
||||
import AutoplayOverlay from '../media/autoplay-overlay/component';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import playAndRetry from '/imports/utils/mediaElementPlayRetry';
|
||||
@ -38,6 +40,32 @@ const MOBILE_HOVER_TIMEOUT = 5000;
|
||||
const MEDIA_FLOW_PROBE_INTERVAL = 500;
|
||||
const SCREEN_SIZE_DISPATCH_INTERVAL = 500;
|
||||
|
||||
const renderPluginItems = (pluginItems, bottom, right) => {
|
||||
if (pluginItems !== undefined) {
|
||||
return (
|
||||
<>
|
||||
{
|
||||
pluginItems.map((pluginItem) => {
|
||||
const returnComponent = (
|
||||
<PluginButtonContainer
|
||||
key={`${pluginItem.type}-${pluginItem.id}-${pluginItem.label}`}
|
||||
dark
|
||||
bottom={bottom}
|
||||
right={right}
|
||||
icon={pluginItem.icon}
|
||||
label={pluginItem.label}
|
||||
onClick={pluginItem.onClick}
|
||||
/>
|
||||
);
|
||||
return returnComponent;
|
||||
})
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (<></>);
|
||||
};
|
||||
|
||||
class ScreenshareComponent extends React.Component {
|
||||
static renderScreenshareContainerInside(mainText) {
|
||||
return (
|
||||
@ -70,6 +98,8 @@ class ScreenshareComponent extends React.Component {
|
||||
this.dispatchScreenShareSize = this.dispatchScreenShareSize.bind(this);
|
||||
this.handleOnMuted = this.handleOnMuted.bind(this);
|
||||
this.dispatchScreenShareSize = this.dispatchScreenShareSize.bind(this);
|
||||
this.renderScreenshareButtons = this.renderScreenshareButtons.bind(this);
|
||||
this.splitPluginItems = this.splitPluginItems.bind(this);
|
||||
this.debouncedDispatchScreenShareSize = debounce(
|
||||
this.dispatchScreenShareSize,
|
||||
SCREEN_SIZE_DISPATCH_INTERVAL,
|
||||
@ -336,14 +366,16 @@ class ScreenshareComponent extends React.Component {
|
||||
if (!ALLOW_FULLSCREEN) return null;
|
||||
|
||||
return (
|
||||
<FullscreenButtonContainer
|
||||
key={uniqueId('fullscreenButton-')}
|
||||
elementName={intl.formatMessage(this.locales.label)}
|
||||
fullscreenRef={this.screenshareContainer}
|
||||
elementId={fullscreenElementId}
|
||||
isFullscreen={fullscreenContext}
|
||||
dark
|
||||
/>
|
||||
<Styled.FullscreenButtonWrapperForScreenshare>
|
||||
<FullscreenButtonContainer
|
||||
key={uniqueId('fullscreenButton-')}
|
||||
elementName={intl.formatMessage(this.locales.label)}
|
||||
fullscreenRef={this.screenshareContainer}
|
||||
elementId={fullscreenElementId}
|
||||
isFullscreen={fullscreenContext}
|
||||
dark
|
||||
/>
|
||||
</Styled.FullscreenButtonWrapperForScreenshare>
|
||||
);
|
||||
}
|
||||
|
||||
@ -410,7 +442,8 @@ class ScreenshareComponent extends React.Component {
|
||||
return [(
|
||||
<Styled.HoverToolbar
|
||||
toolbarStyle={toolbarStyle}
|
||||
key='hover-toolbar-screenshare'>
|
||||
key="hover-toolbar-screenshare"
|
||||
>
|
||||
<VolumeSlider
|
||||
volume={getVolume()}
|
||||
muted={getVolume() === 0}
|
||||
@ -418,8 +451,8 @@ class ScreenshareComponent extends React.Component {
|
||||
onMuted={this.handleOnMuted}
|
||||
/>
|
||||
</Styled.HoverToolbar>
|
||||
),
|
||||
(deviceInfo.isMobile) && this.renderMobileVolumeControlOverlay(),
|
||||
),
|
||||
(deviceInfo.isMobile) && this.renderMobileVolumeControlOverlay(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -446,17 +479,41 @@ class ScreenshareComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
splitPluginItems() {
|
||||
const { pluginScreenshareHelperItems } = this.props;
|
||||
|
||||
return pluginScreenshareHelperItems.reduce((result, item) => {
|
||||
switch (item.position) {
|
||||
case PluginSdk.ScreenshareHelperItemPosition.TOP_RIGHT:
|
||||
result.topRightPluginItems.push(item);
|
||||
break;
|
||||
case PluginSdk.ScreenshareHelperItemPosition.TOP_LEFT:
|
||||
result.topLeftPluginItems.push(item);
|
||||
break;
|
||||
case PluginSdk.ScreenshareHelperItemPosition.BOTTOM_RIGHT:
|
||||
result.bottomRightPluginItems.push(item);
|
||||
break;
|
||||
case PluginSdk.ScreenshareHelperItemPosition.BOTTOM_LEFT:
|
||||
result.bottomLeftPluginItems.push(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}, {
|
||||
topRightPluginItems: [],
|
||||
topLeftPluginItems: [],
|
||||
bottomRightPluginItems: [],
|
||||
bottomLeftPluginItems: [],
|
||||
});
|
||||
}
|
||||
|
||||
renderScreensharePresenter() {
|
||||
const { switched } = this.state;
|
||||
const { isGloballyBroadcasting, intl } = this.props;
|
||||
|
||||
return (
|
||||
<Styled.ScreenshareContainer
|
||||
switched={switched}
|
||||
key="screenshareContainer"
|
||||
ref={(ref) => { this.screenshareContainer = ref; }}
|
||||
>
|
||||
{isGloballyBroadcasting && this.renderSwitchButton()}
|
||||
<>
|
||||
{this.renderVideo(switched)}
|
||||
|
||||
{
|
||||
@ -473,7 +530,7 @@ class ScreenshareComponent extends React.Component {
|
||||
intl.formatMessage(this.locales.presenterLoadingLabel),
|
||||
)
|
||||
}
|
||||
</Styled.ScreenshareContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -482,15 +539,7 @@ class ScreenshareComponent extends React.Component {
|
||||
const { loaded } = this.state;
|
||||
|
||||
return (
|
||||
<Styled.ScreenshareContainer
|
||||
switched
|
||||
key="screenshareContainer"
|
||||
ref={(ref) => {
|
||||
this.screenshareContainer = ref;
|
||||
}}
|
||||
id="screenshareContainer"
|
||||
>
|
||||
{loaded && this.renderFullscreenButton()}
|
||||
<>
|
||||
{this.renderVideo(true)}
|
||||
{loaded && enableVolumeControl && this.renderVolumeSlider() }
|
||||
|
||||
@ -503,12 +552,61 @@ class ScreenshareComponent extends React.Component {
|
||||
: null
|
||||
}
|
||||
</Styled.ScreenshareContainerDefault>
|
||||
</Styled.ScreenshareContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderScreenshareButtons() {
|
||||
const { isPresenter, isGloballyBroadcasting } = this.props;
|
||||
const { loaded } = this.state;
|
||||
const {
|
||||
topRightPluginItems,
|
||||
topLeftPluginItems,
|
||||
bottomRightPluginItems,
|
||||
bottomLeftPluginItems,
|
||||
} = this.splitPluginItems();
|
||||
return (
|
||||
<>
|
||||
<Styled.ScreenshareButtonsContainterWrapper
|
||||
positionYAxis="top"
|
||||
positionXAxis="left"
|
||||
>
|
||||
{renderPluginItems(topLeftPluginItems, false, false)}
|
||||
</Styled.ScreenshareButtonsContainterWrapper>
|
||||
<Styled.ScreenshareButtonsContainterWrapper
|
||||
positionYAxis="top"
|
||||
positionXAxis="right"
|
||||
>
|
||||
{isPresenter
|
||||
// Presenter button:
|
||||
? isGloballyBroadcasting && this.renderSwitchButton()
|
||||
// Non-presenter button:
|
||||
: loaded && this.renderFullscreenButton()}
|
||||
{renderPluginItems(topRightPluginItems, false, true)}
|
||||
</Styled.ScreenshareButtonsContainterWrapper>
|
||||
<Styled.ScreenshareButtonsContainterWrapper
|
||||
positionYAxis="bottom"
|
||||
positionXAxis="left"
|
||||
>
|
||||
{renderPluginItems(bottomLeftPluginItems, true, false)}
|
||||
</Styled.ScreenshareButtonsContainterWrapper>
|
||||
<Styled.ScreenshareButtonsContainterWrapper
|
||||
positionYAxis="bottom"
|
||||
positionXAxis="right"
|
||||
>
|
||||
{renderPluginItems(bottomRightPluginItems, true, true)}
|
||||
</Styled.ScreenshareButtonsContainterWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loaded, autoplayBlocked, mediaFlowing} = this.state;
|
||||
const {
|
||||
loaded,
|
||||
autoplayBlocked,
|
||||
mediaFlowing,
|
||||
switched,
|
||||
} = this.state;
|
||||
const {
|
||||
isPresenter,
|
||||
isGloballyBroadcasting,
|
||||
@ -565,7 +663,19 @@ class ScreenshareComponent extends React.Component {
|
||||
</Styled.SpinnerWrapper>
|
||||
)}
|
||||
{autoplayBlocked ? this.renderAutoplayOverlay() : null}
|
||||
{isPresenter ? this.renderScreensharePresenter() : this.renderScreenshareDefault()}
|
||||
<Styled.ScreenshareContainer
|
||||
switched={isPresenter ? switched : true}
|
||||
key="screenshareContainer"
|
||||
ref={(ref) => {
|
||||
this.screenshareContainer = ref;
|
||||
}}
|
||||
id="screenshareContainer"
|
||||
>
|
||||
{this.renderScreenshareButtons()}
|
||||
{isPresenter
|
||||
? this.renderScreensharePresenter()
|
||||
: this.renderScreenshareDefault()}
|
||||
</Styled.ScreenshareContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -577,6 +687,9 @@ ScreenshareComponent.propTypes = {
|
||||
intl: PropTypes.shape({
|
||||
formatMessage: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
pluginScreenshareHelperItems: PropTypes.arrayOf(PropTypes.objectOf({
|
||||
position: PropTypes.string,
|
||||
})).isRequired,
|
||||
isPresenter: PropTypes.bool.isRequired,
|
||||
layoutContextDispatch: PropTypes.func.isRequired,
|
||||
enableVolumeControl: PropTypes.bool.isRequired,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { useMutation, useReactiveVar } from '@apollo/client';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import {
|
||||
@ -11,6 +11,7 @@ import {
|
||||
useScreenshareHasAudio,
|
||||
useBroadcastContentType,
|
||||
} from './service';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
import ScreenshareComponent from './component';
|
||||
import { layoutSelect, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
@ -93,6 +94,7 @@ const ScreenshareContainer = (props) => {
|
||||
const screenShare = layoutSelectOutput((i) => i.screenShare);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
const { pluginsExtensibleAreasAggregatedState } = useContext(PluginsContext);
|
||||
|
||||
const { element } = fullscreen;
|
||||
const fullscreenElementId = 'Screenshare';
|
||||
@ -137,11 +139,18 @@ const ScreenshareContainer = (props) => {
|
||||
const isCameraAsContentBroadcasting = useIsCameraAsContentBroadcasting();
|
||||
const hasAudio = useScreenshareHasAudio();
|
||||
|
||||
let pluginScreenshareHelperItems = [];
|
||||
if (pluginsExtensibleAreasAggregatedState.screenshareHelperItems) {
|
||||
pluginScreenshareHelperItems = [
|
||||
...pluginsExtensibleAreasAggregatedState.screenshareHelperItems,
|
||||
];
|
||||
}
|
||||
if (isScreenBroadcasting || isCameraAsContentBroadcasting) {
|
||||
return (
|
||||
<ScreenshareComponent
|
||||
{
|
||||
...{
|
||||
pluginScreenshareHelperItems,
|
||||
layoutContextDispatch,
|
||||
...props,
|
||||
...screenShare,
|
||||
|
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import Styled from './styles';
|
||||
|
||||
interface PluginButtonComponentProps {
|
||||
dark: boolean;
|
||||
bottom: boolean;
|
||||
right: boolean;
|
||||
icon: string;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const PluginButtonComponent = ({
|
||||
dark = false,
|
||||
bottom = false,
|
||||
onClick = () => {},
|
||||
right,
|
||||
icon,
|
||||
label,
|
||||
}: PluginButtonComponentProps) => (
|
||||
<Styled.PluginButtonWrapper
|
||||
{...{
|
||||
dark,
|
||||
bottom,
|
||||
right,
|
||||
}}
|
||||
>
|
||||
<Styled.PluginButton
|
||||
color="default"
|
||||
icon={icon}
|
||||
size="sm"
|
||||
onClick={onClick}
|
||||
hideLabel
|
||||
label={label}
|
||||
data-test="switchButton"
|
||||
/>
|
||||
</Styled.PluginButtonWrapper>
|
||||
);
|
||||
|
||||
export default PluginButtonComponent;
|
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import PluginButtonComponent from './component';
|
||||
|
||||
interface PluginButtonContainerProps {
|
||||
dark: boolean;
|
||||
bottom: boolean;
|
||||
right: boolean;
|
||||
icon: string;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const PluginButtonContainer = (props: PluginButtonContainerProps) => {
|
||||
const {
|
||||
dark,
|
||||
bottom,
|
||||
right,
|
||||
icon,
|
||||
label,
|
||||
onClick,
|
||||
} = props;
|
||||
return (
|
||||
<PluginButtonComponent
|
||||
dark={dark}
|
||||
bottom={bottom}
|
||||
right={right}
|
||||
icon={icon}
|
||||
label={label}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginButtonContainer;
|
@ -0,0 +1,64 @@
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
colorWhite,
|
||||
colorTransparent,
|
||||
colorBlack,
|
||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
|
||||
const PluginButtonWrapper = styled.div`
|
||||
background-color: ${colorTransparent};
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
z-index: 2;
|
||||
margin: 2px;
|
||||
[dir="rtl"] & {
|
||||
right: auto;
|
||||
left: 0;
|
||||
${({ fullScreenEnabled }) => fullScreenEnabled && `
|
||||
left: 1.75rem;
|
||||
`}
|
||||
}
|
||||
[class*="presentationZoomControls"] & {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
${({ dark }) => dark && `
|
||||
background-color: rgba(0,0,0,.3);
|
||||
|
||||
& button i {
|
||||
color: ${colorWhite};
|
||||
}
|
||||
`}
|
||||
|
||||
${({ dark }) => !dark && `
|
||||
background-color: ${colorTransparent};
|
||||
|
||||
& button i {
|
||||
color: ${colorBlack};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const PluginButton = styled(Button)`
|
||||
padding: 5px;
|
||||
|
||||
&,
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: ${colorTransparent} !important;
|
||||
border: none !important;
|
||||
|
||||
i {
|
||||
border: none !important;
|
||||
background-color: ${colorTransparent} !important;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default {
|
||||
PluginButtonWrapper,
|
||||
PluginButton,
|
||||
};
|
@ -90,7 +90,37 @@ const HoverToolbar = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
const ScreenshareButtonsContainterWrapper = styled.div`
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
width: 50%;
|
||||
|
||||
${({ positionXAxis }) => positionXAxis === 'right' && `
|
||||
right: 0;
|
||||
flex-direction: row-reverse;
|
||||
`}
|
||||
${({ positionXAxis }) => positionXAxis === 'left' && `
|
||||
left: 0;
|
||||
`}
|
||||
|
||||
${({ positionYAxis }) => positionYAxis === 'top' && `
|
||||
top: 0;
|
||||
`}
|
||||
${({ positionYAxis }) => positionYAxis === 'bottom' && `
|
||||
bottom: 0;
|
||||
`}
|
||||
`;
|
||||
|
||||
const FullscreenButtonWrapperForScreenshare = styled.div`
|
||||
& > * {
|
||||
position: relative !important;
|
||||
}
|
||||
`;
|
||||
|
||||
export default {
|
||||
FullscreenButtonWrapperForScreenshare,
|
||||
ScreenshareButtonsContainterWrapper,
|
||||
ScreenshareContainerInside,
|
||||
MainText,
|
||||
ScreenshareVideo,
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
|
||||
const SwitchButtonWrapper = styled.div`
|
||||
position: absolute;
|
||||
position: relative;
|
||||
right: 0;
|
||||
left: auto;
|
||||
background-color: ${colorTransparent};
|
||||
|
Loading…
Reference in New Issue
Block a user