[plugin-sdk-camera-settings] - changes in review
This commit is contained in:
commit
4129215741
2
.github/workflows/automated-tests.yml
vendored
2
.github/workflows/automated-tests.yml
vendored
@ -252,7 +252,7 @@ jobs:
|
||||
apt --purge -y remove apache2-bin
|
||||
'
|
||||
- name: Install BBB
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 25
|
||||
run: |
|
||||
sudo -i <<EOF
|
||||
set -e
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import CaptionsButtonContainer from '/imports/ui/components/captions/button/container';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import Styled from './styles';
|
||||
import ActionsDropdown from './actions-dropdown/container';
|
||||
import AudioCaptionsButtonContainer from '/imports/ui/components/audio/captions/button/container';
|
||||
@ -12,6 +13,7 @@ import JoinVideoOptionsContainer from '../video-provider/video-button/container'
|
||||
import PresentationOptionsContainer from './presentation-options/component';
|
||||
import RaiseHandDropdownContainer from './raise-hand/container';
|
||||
import { isPresentationEnabled } from '/imports/ui/services/features';
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
|
||||
class ActionsBar extends PureComponent {
|
||||
constructor(props) {
|
||||
@ -24,12 +26,53 @@ class ActionsBar extends PureComponent {
|
||||
this.setCaptionsReaderMenuModalIsOpen = this.setCaptionsReaderMenuModalIsOpen.bind(this);
|
||||
this.setRenderRaiseHand = this.renderRaiseHand.bind(this);
|
||||
this.actionsBarRef = React.createRef();
|
||||
this.renderPluginsActionBarItems = this.renderPluginsActionBarItems.bind(this);
|
||||
}
|
||||
|
||||
setCaptionsReaderMenuModalIsOpen(value) {
|
||||
this.setState({ isCaptionsReaderMenuModalOpen: value })
|
||||
}
|
||||
|
||||
renderPluginsActionBarItems(position) {
|
||||
const { actionBarItems } = this.props;
|
||||
return (
|
||||
<>
|
||||
{
|
||||
actionBarItems.filter((plugin) => plugin.position === position).map((plugin, index) => {
|
||||
let actionBarItemToReturn;
|
||||
switch (plugin.type) {
|
||||
case PluginSdk.ActionsBarItemType.BUTTON:
|
||||
actionBarItemToReturn = (
|
||||
<Button
|
||||
key={`${plugin.type}-${plugin.id}`}
|
||||
onClick={plugin.onClick}
|
||||
hideLabel
|
||||
color="primary"
|
||||
icon={plugin.icon}
|
||||
size="lg"
|
||||
circle
|
||||
label={plugin.tooltip}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case PluginSdk.ActionsBarItemType.SEPARATOR:
|
||||
actionBarItemToReturn = (
|
||||
<Styled.Separator
|
||||
key={`${plugin.type}-${plugin.id}`}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
actionBarItemToReturn = null;
|
||||
break;
|
||||
}
|
||||
return actionBarItemToReturn;
|
||||
})
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderRaiseHand() {
|
||||
const {
|
||||
isReactionsButtonEnabled, isRaiseHandButtonEnabled, setEmojiStatus, currentUser, intl,
|
||||
@ -138,6 +181,7 @@ class ActionsBar extends PureComponent {
|
||||
: null }
|
||||
</Styled.Left>
|
||||
<Styled.Center>
|
||||
{this.renderPluginsActionBarItems(PluginSdk.ActionsBarPosition.LEFT)}
|
||||
<AudioControlsContainer />
|
||||
{enableVideo
|
||||
? (
|
||||
@ -149,7 +193,8 @@ class ActionsBar extends PureComponent {
|
||||
isMeteorConnected,
|
||||
}}
|
||||
/>
|
||||
{isRaiseHandButtonCentered && this.renderRaiseHand()}
|
||||
{isRaiseHandButtonCentered && this.renderRaiseHand()}
|
||||
{this.renderPluginsActionBarItems(PluginSdk.ActionsBarPosition.RIGHT)}
|
||||
</Styled.Center>
|
||||
<Styled.Right>
|
||||
{ shouldShowOptionsButton ?
|
||||
|
@ -16,6 +16,7 @@ import TimerService from '/imports/ui/components/timer/service';
|
||||
import { layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import { isExternalVideoEnabled, isPollingEnabled, isPresentationEnabled } from '/imports/ui/services/features';
|
||||
import { isScreenBroadcasting, isCameraAsContentBroadcasting } from '/imports/ui/components/screenshare/service';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
import MediaService from '../media/service';
|
||||
|
||||
@ -24,6 +25,16 @@ const ActionsBarContainer = (props) => {
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
let actionBarItems = [];
|
||||
if (pluginsProvidedAggregatedState.actionsBarItems) {
|
||||
actionBarItems = [
|
||||
...pluginsProvidedAggregatedState.actionsBarItems,
|
||||
];
|
||||
}
|
||||
|
||||
const { users } = usingUsersContext;
|
||||
|
||||
const currentUser = { userId: Auth.userID, emoji: users[Auth.meetingID][Auth.userID].emoji };
|
||||
@ -40,6 +51,7 @@ const ActionsBarContainer = (props) => {
|
||||
layoutContextDispatch,
|
||||
actionsBarStyle,
|
||||
amIPresenter,
|
||||
actionBarItems,
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
@ -1,10 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useShortcut } from '/imports/ui/core/hooks/useShortcut';
|
||||
import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||
import { MenuSeparatorItemType, MenuOptionItemType } from '/imports/ui/components/common/menu/menuTypes';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import Styled from '../styles';
|
||||
import {
|
||||
handleLeaveAudio,
|
||||
@ -15,6 +16,7 @@ import {
|
||||
} from '../service';
|
||||
import Mutetoggle from './muteToggle';
|
||||
import ListenOnly from './listenOnly';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
const AUDIO_INPUT = 'audioinput';
|
||||
const AUDIO_OUTPUT = 'audiooutput';
|
||||
@ -89,6 +91,16 @@ export const LiveSelection: React.FC<LiveSelectionProps> = ({
|
||||
|
||||
const leaveAudioShourtcut = useShortcut('leaveAudio');
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
let audioSettingsDropdownItems = [] as PluginSdk.AudioSettingsDropdownItem[];
|
||||
if (pluginsProvidedAggregatedState.audioSettingsDropdownItems) {
|
||||
audioSettingsDropdownItems = [
|
||||
...pluginsProvidedAggregatedState.audioSettingsDropdownItems,
|
||||
];
|
||||
}
|
||||
|
||||
const renderDeviceList = useCallback((
|
||||
deviceKind: string,
|
||||
list: MediaDeviceInfo[],
|
||||
@ -194,6 +206,33 @@ export const LiveSelection: React.FC<LiveSelectionProps> = ({
|
||||
isSeparator: true,
|
||||
})
|
||||
.concat(leaveAudioOption);
|
||||
|
||||
audioSettingsDropdownItems.forEach((audioSettingsDropdownItem:
|
||||
PluginSdk.AudioSettingsDropdownItem) => {
|
||||
switch (audioSettingsDropdownItem.type) {
|
||||
case PluginSdk.AudioSettingsDropdownItemType.OPTION: {
|
||||
const audioSettingsDropdownOption = audioSettingsDropdownItem as PluginSdk.AudioSettingsDropdownOption;
|
||||
dropdownListComplete.push({
|
||||
label: audioSettingsDropdownOption.label,
|
||||
iconRight: audioSettingsDropdownOption.icon,
|
||||
onClick: audioSettingsDropdownOption.onClick,
|
||||
key: audioSettingsDropdownOption.id,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PluginSdk.AudioSettingsDropdownItemType.SEPARATOR: {
|
||||
const audioSettingsDropdownSeparator = audioSettingsDropdownItem as PluginSdk.AudioSettingsDropdownOption;
|
||||
dropdownListComplete.push({
|
||||
isSeparator: true,
|
||||
key: audioSettingsDropdownSeparator.id,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const customStyles = { top: '-1rem' };
|
||||
const { isMobile } = deviceInfo;
|
||||
return (
|
||||
|
@ -2,17 +2,19 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import Styled from './styles';
|
||||
import RecordingIndicator from './recording-indicator/container';
|
||||
import TalkingIndicatorContainer from '/imports/ui/components/nav-bar/talking-indicator/container';
|
||||
import ConnectionStatusButton from '/imports/ui/components/connection-status/button/container';
|
||||
import ConnectionStatusService from '/imports/ui/components/connection-status/service';
|
||||
import { addNewAlert } from '/imports/ui/components/screenreader-alert/service';
|
||||
import SettingsDropdownContainer from './settings-dropdown/container';
|
||||
import OptionsDropdownContainer from './options-dropdown/container';
|
||||
import TimerIndicatorContainer from '/imports/ui/components/timer/indicator/container';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import { PANELS, ACTIONS } from '../layout/enums';
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
import { isEqual } from 'radash';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
@ -45,6 +47,9 @@ const propTypes = {
|
||||
breakoutNum: PropTypes.number,
|
||||
breakoutName: PropTypes.string,
|
||||
meetingName: PropTypes.string,
|
||||
pluginNavBarItems: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -53,6 +58,70 @@ const defaultProps = {
|
||||
shortcuts: '',
|
||||
};
|
||||
|
||||
const renderPluginItems = (pluginItems) => (
|
||||
<>
|
||||
{
|
||||
pluginItems.map((pluginItem) => {
|
||||
let returnComponent;
|
||||
switch (pluginItem.type) {
|
||||
case PluginSdk.NavBarItemType.BUTTON:
|
||||
returnComponent = (
|
||||
<Styled.PluginComponentWrapper
|
||||
key={pluginItem.id}
|
||||
>
|
||||
<Button
|
||||
icon={pluginItem.icon}
|
||||
label={pluginItem.label}
|
||||
aria-label={pluginItem.tooltip}
|
||||
color="primary"
|
||||
tooltip={pluginItem.tooltip}
|
||||
onClick={pluginItem.onClick}
|
||||
/>
|
||||
</Styled.PluginComponentWrapper>
|
||||
);
|
||||
break;
|
||||
case PluginSdk.NavBarItemType.INFO:
|
||||
returnComponent = (
|
||||
<Styled.PluginComponentWrapper
|
||||
key={pluginItem.id}
|
||||
tooltip={pluginItem.tooltip}
|
||||
>
|
||||
<Styled.PluginInfoComponent>
|
||||
{pluginItem.label}
|
||||
</Styled.PluginInfoComponent>
|
||||
</Styled.PluginComponentWrapper>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
returnComponent = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pluginItem.hasSeparator) {
|
||||
switch (pluginItem.position) {
|
||||
case PluginSdk.NavBarItemPosition.RIGHT:
|
||||
returnComponent = (
|
||||
<>
|
||||
{returnComponent}
|
||||
<Styled.PluginSeparatorWrapper>|</Styled.PluginSeparatorWrapper>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
returnComponent = (
|
||||
<>
|
||||
<Styled.PluginSeparatorWrapper>|</Styled.PluginSeparatorWrapper>
|
||||
{returnComponent}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return returnComponent;
|
||||
})
|
||||
}
|
||||
</>
|
||||
);
|
||||
class NavBar extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -62,6 +131,7 @@ class NavBar extends Component {
|
||||
}
|
||||
|
||||
this.handleToggleUserList = this.handleToggleUserList.bind(this);
|
||||
this.splitPluginItems = this.splitPluginItems.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -156,6 +226,31 @@ class NavBar extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
splitPluginItems() {
|
||||
const { pluginNavBarItems } = this.props;
|
||||
|
||||
return pluginNavBarItems.reduce((result, item) => {
|
||||
switch (item.position) {
|
||||
case PluginSdk.NavBarItemPosition.LEFT:
|
||||
result.leftPluginItems.push(item);
|
||||
break;
|
||||
case PluginSdk.NavBarItemPosition.CENTER:
|
||||
result.centerPluginItems.push(item);
|
||||
break;
|
||||
case PluginSdk.NavBarItemPosition.RIGHT:
|
||||
result.rightPluginItems.push(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}, {
|
||||
leftPluginItems: [],
|
||||
centerPluginItems: [],
|
||||
rightPluginItems: [],
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
hasUnreadMessages,
|
||||
@ -189,6 +284,8 @@ class NavBar extends Component {
|
||||
}
|
||||
});
|
||||
|
||||
const { leftPluginItems, centerPluginItems, rightPluginItems } = this.splitPluginItems();
|
||||
|
||||
return (
|
||||
<Styled.Navbar
|
||||
id="Navbar"
|
||||
@ -233,6 +330,7 @@ class NavBar extends Component {
|
||||
&& <Styled.ArrowRight iconName="right_arrow" />}
|
||||
{isExpanded && document.dir === 'rtl'
|
||||
&& <Styled.ArrowRight iconName="right_arrow" />}
|
||||
{renderPluginItems(leftPluginItems)}
|
||||
</Styled.Left>
|
||||
<Styled.Center>
|
||||
<Styled.PresentationTitle data-test="presentationTitle">
|
||||
@ -242,10 +340,12 @@ class NavBar extends Component {
|
||||
amIModerator={amIModerator}
|
||||
currentUserId={currentUserId}
|
||||
/>
|
||||
{renderPluginItems(centerPluginItems)}
|
||||
</Styled.Center>
|
||||
<Styled.Right>
|
||||
{renderPluginItems(rightPluginItems)}
|
||||
{ConnectionStatusService.isEnabled() ? <ConnectionStatusButton /> : null}
|
||||
<SettingsDropdownContainer amIModerator={amIModerator} />
|
||||
<OptionsDropdownContainer amIModerator={amIModerator} />
|
||||
</Styled.Right>
|
||||
</Styled.Top>
|
||||
<Styled.Bottom>
|
||||
|
@ -12,6 +12,7 @@ import { UsersContext } from '/imports/ui/components/components-data/users-conte
|
||||
import NotesService from '/imports/ui/components/notes/service';
|
||||
import NavBar from './component';
|
||||
import { layoutSelectInput, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
import { PANELS } from '/imports/ui/components/layout/enums';
|
||||
|
||||
const PUBLIC_CONFIG = Meteor.settings.public;
|
||||
@ -31,6 +32,7 @@ const checkUnreadMessages = ({
|
||||
const NavBarContainer = ({ children, ...props }) => {
|
||||
const usingChatContext = useContext(ChatContext);
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { pluginsProvidedAggregatedState } = useContext(PluginsContext);
|
||||
const usingGroupChatContext = useContext(GroupChatContext);
|
||||
const { chats: groupChatsMessages } = usingChatContext;
|
||||
const { users } = usingUsersContext;
|
||||
@ -62,6 +64,13 @@ const NavBarContainer = ({ children, ...props }) => {
|
||||
|
||||
if (hideNavBar || navBar.display === false) return null;
|
||||
|
||||
let pluginNavBarItems = [];
|
||||
if (pluginsProvidedAggregatedState.navBarItems) {
|
||||
pluginNavBarItems = [
|
||||
...pluginsProvidedAggregatedState.navBarItems,
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<NavBar
|
||||
{...{
|
||||
@ -76,6 +85,7 @@ const NavBarContainer = ({ children, ...props }) => {
|
||||
isExpanded,
|
||||
activeChats,
|
||||
currentUserId: Auth.userID,
|
||||
pluginNavBarItems,
|
||||
...rest,
|
||||
}}
|
||||
style={{ ...navBar }}
|
||||
@ -124,4 +134,4 @@ export default withTracker(() => {
|
||||
meetingName,
|
||||
unread,
|
||||
};
|
||||
})(NavBarContainer);
|
||||
})(NavBarContainer);
|
||||
|
@ -5,87 +5,88 @@ import EndMeetingConfirmationContainer from '/imports/ui/components/end-meeting-
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import AboutContainer from '/imports/ui/components/about/container';
|
||||
import MobileAppModal from '/imports/ui/components/mobile-app-modal/container';
|
||||
import SettingsMenuContainer from '/imports/ui/components/settings/container';
|
||||
import OptionsMenuContainer from '/imports/ui/components/settings/container';
|
||||
import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||
import ShortcutHelpComponent from '/imports/ui/components/shortcut-help/component';
|
||||
import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
|
||||
import FullscreenService from '/imports/ui/components/common/fullscreen-button/service';
|
||||
import { colorDanger, colorWhite } from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import Styled from './styles';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
optionsLabel: {
|
||||
id: 'app.navBar.settingsDropdown.optionsLabel',
|
||||
id: 'app.navBar.optionsDropdown.optionsLabel',
|
||||
description: 'Options button label',
|
||||
},
|
||||
fullscreenLabel: {
|
||||
id: 'app.navBar.settingsDropdown.fullscreenLabel',
|
||||
id: 'app.navBar.optionsDropdown.fullscreenLabel',
|
||||
description: 'Make fullscreen option label',
|
||||
},
|
||||
settingsLabel: {
|
||||
id: 'app.navBar.settingsDropdown.settingsLabel',
|
||||
id: 'app.navBar.optionsDropdown.settingsLabel',
|
||||
description: 'Open settings option label',
|
||||
},
|
||||
aboutLabel: {
|
||||
id: 'app.navBar.settingsDropdown.aboutLabel',
|
||||
id: 'app.navBar.optionsDropdown.aboutLabel',
|
||||
description: 'About option label',
|
||||
},
|
||||
aboutDesc: {
|
||||
id: 'app.navBar.settingsDropdown.aboutDesc',
|
||||
id: 'app.navBar.optionsDropdown.aboutDesc',
|
||||
description: 'Describes about option',
|
||||
},
|
||||
leaveSessionLabel: {
|
||||
id: 'app.navBar.settingsDropdown.leaveSessionLabel',
|
||||
id: 'app.navBar.optionsDropdown.leaveSessionLabel',
|
||||
description: 'Leave session button label',
|
||||
},
|
||||
fullscreenDesc: {
|
||||
id: 'app.navBar.settingsDropdown.fullscreenDesc',
|
||||
id: 'app.navBar.optionsDropdown.fullscreenDesc',
|
||||
description: 'Describes fullscreen option',
|
||||
},
|
||||
settingsDesc: {
|
||||
id: 'app.navBar.settingsDropdown.settingsDesc',
|
||||
id: 'app.navBar.optionsDropdown.settingsDesc',
|
||||
description: 'Describes settings option',
|
||||
},
|
||||
leaveSessionDesc: {
|
||||
id: 'app.navBar.settingsDropdown.leaveSessionDesc',
|
||||
id: 'app.navBar.optionsDropdown.leaveSessionDesc',
|
||||
description: 'Describes leave session option',
|
||||
},
|
||||
exitFullscreenDesc: {
|
||||
id: 'app.navBar.settingsDropdown.exitFullscreenDesc',
|
||||
id: 'app.navBar.optionsDropdown.exitFullscreenDesc',
|
||||
description: 'Describes exit fullscreen option',
|
||||
},
|
||||
exitFullscreenLabel: {
|
||||
id: 'app.navBar.settingsDropdown.exitFullscreenLabel',
|
||||
id: 'app.navBar.optionsDropdown.exitFullscreenLabel',
|
||||
description: 'Exit fullscreen option label',
|
||||
},
|
||||
hotkeysLabel: {
|
||||
id: 'app.navBar.settingsDropdown.hotkeysLabel',
|
||||
id: 'app.navBar.optionsDropdown.hotkeysLabel',
|
||||
description: 'Hotkeys options label',
|
||||
},
|
||||
hotkeysDesc: {
|
||||
id: 'app.navBar.settingsDropdown.hotkeysDesc',
|
||||
id: 'app.navBar.optionsDropdown.hotkeysDesc',
|
||||
description: 'Describes hotkeys option',
|
||||
},
|
||||
helpLabel: {
|
||||
id: 'app.navBar.settingsDropdown.helpLabel',
|
||||
id: 'app.navBar.optionsDropdown.helpLabel',
|
||||
description: 'Help options label',
|
||||
},
|
||||
openAppLabel: {
|
||||
id: 'app.navBar.settingsDropdown.openAppLabel',
|
||||
id: 'app.navBar.optionsDropdown.openAppLabel',
|
||||
description: 'Open mobile app label',
|
||||
},
|
||||
helpDesc: {
|
||||
id: 'app.navBar.settingsDropdown.helpDesc',
|
||||
id: 'app.navBar.optionsDropdown.helpDesc',
|
||||
description: 'Describes help option',
|
||||
},
|
||||
endMeetingLabel: {
|
||||
id: 'app.navBar.settingsDropdown.endMeetingLabel',
|
||||
id: 'app.navBar.optionsDropdown.endMeetingLabel',
|
||||
description: 'End meeting options label',
|
||||
},
|
||||
endMeetingDesc: {
|
||||
id: 'app.navBar.settingsDropdown.endMeetingDesc',
|
||||
id: 'app.navBar.optionsDropdown.endMeetingDesc',
|
||||
description: 'Describes settings option closing the current meeting',
|
||||
},
|
||||
startCaption: {
|
||||
@ -113,6 +114,10 @@ const propTypes = {
|
||||
audioCaptionsActive: PropTypes.bool.isRequired,
|
||||
audioCaptionsSet: PropTypes.func.isRequired,
|
||||
isMobile: PropTypes.bool.isRequired,
|
||||
optionsDropdownItems: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -128,14 +133,14 @@ const BBB_TABLET_APP_CONFIG = Meteor.settings.public.app.bbbTabletApp;
|
||||
const { isSafari, isTabletApp } = browserInfo;
|
||||
const FULLSCREEN_CHANGE_EVENT = isSafari ? 'webkitfullscreenchange' : 'fullscreenchange';
|
||||
|
||||
class SettingsDropdown extends PureComponent {
|
||||
class OptionsDropdown extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAboutModalOpen: false,
|
||||
isShortcutHelpModalOpen: false,
|
||||
isSettingsMenuModalOpen: false,
|
||||
isOptionsMenuModalOpen: false,
|
||||
isEndMeetingConfirmationModalOpen: false,
|
||||
isMobileAppModalOpen:false,
|
||||
isFullscreen: false,
|
||||
@ -146,7 +151,7 @@ class SettingsDropdown extends PureComponent {
|
||||
|
||||
this.leaveSession = this.leaveSession.bind(this);
|
||||
this.onFullscreenChange = this.onFullscreenChange.bind(this);
|
||||
this.setSettingsMenuModalIsOpen = this.setSettingsMenuModalIsOpen.bind(this);
|
||||
this.setOptionsMenuModalIsOpen = this.setOptionsMenuModalIsOpen.bind(this);
|
||||
this.setEndMeetingConfirmationModalIsOpen = this.setEndMeetingConfirmationModalIsOpen.bind(this);
|
||||
this.setMobileAppModalIsOpen = this.setMobileAppModalIsOpen.bind(this);
|
||||
this.setAboutModalIsOpen = this.setAboutModalIsOpen.bind(this);
|
||||
@ -217,8 +222,8 @@ class SettingsDropdown extends PureComponent {
|
||||
this.setState({isShortcutHelpModalOpen: value})
|
||||
}
|
||||
|
||||
setSettingsMenuModalIsOpen(value) {
|
||||
this.setState({isSettingsMenuModalOpen: value})
|
||||
setOptionsMenuModalIsOpen(value) {
|
||||
this.setState({isOptionsMenuModalOpen: value})
|
||||
}
|
||||
|
||||
setEndMeetingConfirmationModalIsOpen(value) {
|
||||
@ -232,7 +237,7 @@ class SettingsDropdown extends PureComponent {
|
||||
renderMenuItems() {
|
||||
const {
|
||||
intl, amIModerator, isBreakoutRoom, isMeteorConnected, audioCaptionsEnabled,
|
||||
audioCaptionsActive, audioCaptionsSet, isMobile,
|
||||
audioCaptionsActive, audioCaptionsSet, isMobile, optionsDropdownItems,
|
||||
} = this.props;
|
||||
|
||||
const { isIos } = deviceInfo;
|
||||
@ -256,7 +261,7 @@ class SettingsDropdown extends PureComponent {
|
||||
dataTest: 'settings',
|
||||
label: intl.formatMessage(intlMessages.settingsLabel),
|
||||
description: intl.formatMessage(intlMessages.settingsDesc),
|
||||
onClick: () => this.setSettingsMenuModalIsOpen(true),
|
||||
onClick: () => this.setOptionsMenuModalIsOpen(true),
|
||||
},
|
||||
{
|
||||
key: 'list-item-about',
|
||||
@ -324,6 +329,27 @@ class SettingsDropdown extends PureComponent {
|
||||
},
|
||||
);
|
||||
|
||||
optionsDropdownItems.forEach((item) => {
|
||||
switch (item.type) {
|
||||
case PluginSdk.OptionsDropdownItemType.OPTION:
|
||||
this.menuItems.push({
|
||||
key: item.id,
|
||||
icon: item.icon,
|
||||
onClick: item.onClick,
|
||||
label: item.label,
|
||||
});
|
||||
break;
|
||||
case PluginSdk.OptionsDropdownItemType.SEPARATOR:
|
||||
this.menuItems.push({
|
||||
key: item.id,
|
||||
isSeparator: true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (allowLogoutSetting && isMeteorConnected) {
|
||||
this.menuItems.push(
|
||||
{
|
||||
@ -376,7 +402,7 @@ class SettingsDropdown extends PureComponent {
|
||||
isRTL,
|
||||
} = this.props;
|
||||
|
||||
const { isAboutModalOpen, isShortcutHelpModalOpen, isSettingsMenuModalOpen,
|
||||
const { isAboutModalOpen, isShortcutHelpModalOpen, isOptionsMenuModalOpen,
|
||||
isEndMeetingConfirmationModalOpen, isMobileAppModalOpen, } = this.state;
|
||||
|
||||
const customStyles = { top: '1rem' };
|
||||
@ -417,8 +443,8 @@ class SettingsDropdown extends PureComponent {
|
||||
AboutContainer)}
|
||||
{this.renderModal(isShortcutHelpModalOpen, this.setShortcutHelpModalIsOpen,
|
||||
"low", ShortcutHelpComponent)}
|
||||
{this.renderModal(isSettingsMenuModalOpen, this.setSettingsMenuModalIsOpen,
|
||||
"low", SettingsMenuContainer)}
|
||||
{this.renderModal(isOptionsMenuModalOpen, this.setOptionsMenuModalIsOpen,
|
||||
"low", OptionsMenuContainer)}
|
||||
{this.renderModal(isEndMeetingConfirmationModalOpen, this.setEndMeetingConfirmationModalIsOpen,
|
||||
"low", EndMeetingConfirmationContainer)}
|
||||
{this.renderModal(isMobileAppModalOpen, this.setMobileAppModalIsOpen, "low",
|
||||
@ -427,6 +453,6 @@ class SettingsDropdown extends PureComponent {
|
||||
);
|
||||
}
|
||||
}
|
||||
SettingsDropdown.propTypes = propTypes;
|
||||
SettingsDropdown.defaultProps = defaultProps;
|
||||
export default withShortcutHelper(injectIntl(SettingsDropdown), 'openOptions');
|
||||
OptionsDropdown.propTypes = propTypes;
|
||||
OptionsDropdown.defaultProps = defaultProps;
|
||||
export default withShortcutHelper(injectIntl(OptionsDropdown), 'openOptions');
|
@ -1,26 +1,38 @@
|
||||
import React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import SettingsDropdown from './component';
|
||||
import OptionsDropdown from './component';
|
||||
import audioCaptionsService from '/imports/ui/components/audio/captions/service';
|
||||
import FullscreenService from '/imports/ui/components/common/fullscreen-button/service';
|
||||
import { meetingIsBreakout } from '/imports/ui/components/app/service';
|
||||
import { layoutSelectInput, layoutSelect } from '../../layout/context';
|
||||
import { SMALL_VIEWPORT_BREAKPOINT } from '../../layout/enums';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
const { isIphone } = deviceInfo;
|
||||
const { isSafari, isValidSafariVersion } = browserInfo;
|
||||
|
||||
const noIOSFullscreen = !!(((isSafari && !isValidSafariVersion) || isIphone));
|
||||
|
||||
const SettingsDropdownContainer = (props) => {
|
||||
const OptionsDropdownContainer = (props) => {
|
||||
const { width: browserWidth } = layoutSelectInput((i) => i.browser);
|
||||
const isMobile = browserWidth <= SMALL_VIEWPORT_BREAKPOINT;
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const { pluginsProvidedAggregatedState } = useContext(PluginsContext);
|
||||
let optionsDropdownItems = [];
|
||||
if (pluginsProvidedAggregatedState.optionsDropdownItems) {
|
||||
optionsDropdownItems = [
|
||||
...pluginsProvidedAggregatedState.optionsDropdownItems,
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsDropdown {...{ isMobile, isRTL, ...props }} />
|
||||
<OptionsDropdown {...{
|
||||
isMobile, isRTL, optionsDropdownItems, ...props,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -38,4 +50,4 @@ export default withTracker((props) => {
|
||||
isBreakoutRoom: meetingIsBreakout(),
|
||||
isDropdownOpen: Session.get('dropdownOpen'),
|
||||
};
|
||||
})(SettingsDropdownContainer);
|
||||
})(OptionsDropdownContainer);
|
@ -6,6 +6,7 @@ import {
|
||||
colorDanger,
|
||||
colorGrayDark,
|
||||
colorBackground,
|
||||
colorGray,
|
||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import { fontSizeBase } from '/imports/ui/stylesheets/styled-components/typography';
|
||||
import { phoneLandscape, smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
||||
@ -79,6 +80,28 @@ const PresentationTitle = styled.h1`
|
||||
}
|
||||
`;
|
||||
|
||||
const PluginInfoComponent = styled.h1`
|
||||
font-weight: 400;
|
||||
color: ${colorWhite};
|
||||
font-size: ${fontSizeBase};
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 30vw;
|
||||
`;
|
||||
|
||||
const PluginComponentWrapper = styled.div`
|
||||
margin: 0 .5rem;
|
||||
`;
|
||||
|
||||
const PluginSeparatorWrapper = styled.div`
|
||||
color: ${colorGray};
|
||||
font-size: ${fontSizeBase};
|
||||
margin: 0 1rem;
|
||||
`;
|
||||
|
||||
const Right = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -128,4 +151,7 @@ export default {
|
||||
Right,
|
||||
Bottom,
|
||||
NavbarToggleButton,
|
||||
PluginInfoComponent,
|
||||
PluginComponentWrapper,
|
||||
PluginSeparatorWrapper,
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ const PluginLoaderContainer = (props: PluginLoaderContainerProps) => {
|
||||
logger.info(`Loaded plugin ${plugin.name}`);
|
||||
};
|
||||
script.onerror = (err) => {
|
||||
logger.info(`Error when loading plugin ${plugin.name}, error: ${err}`);
|
||||
logger.error(`Error when loading plugin ${plugin.name}, error: `, err);
|
||||
};
|
||||
script.src = plugin.url;
|
||||
script.setAttribute('uuid', div.id);
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const ActionBarPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
actionBarItems,
|
||||
setActionBarItems,
|
||||
] = useState<PluginSdk.ActionsBarItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].actionsBarItems = actionBarItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedActionBarItems = (
|
||||
[] as PluginSdk.ActionsBarItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.actionsBarItems),
|
||||
);
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
actionsBarItems: aggregatedActionBarItems,
|
||||
},
|
||||
);
|
||||
}, [actionBarItems]);
|
||||
|
||||
pluginApi.setActionsBarItems = (items: PluginSdk.ActionsBarItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.ActionsBarItem[];
|
||||
return setActionBarItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default ActionBarPluginStateContainer;
|
@ -0,0 +1,53 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const AudioSettingsDropdownPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
audioSettingsDropdownItems,
|
||||
setAudioSettingsDropdownItems,
|
||||
] = useState<PluginSdk.AudioSettingsDropdownItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].audioSettingsDropdownItems = audioSettingsDropdownItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedAudioSettingsDropdownItems = ([] as PluginSdk.AudioSettingsDropdownItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.audioSettingsDropdownItems),
|
||||
);
|
||||
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
audioSettingsDropdownItems: aggregatedAudioSettingsDropdownItems,
|
||||
},
|
||||
);
|
||||
}, [audioSettingsDropdownItems]);
|
||||
|
||||
pluginApi.setAudioSettingsDropdownItems = (items: PluginSdk.AudioSettingsDropdownItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.AudioSettingsDropdownItem[];
|
||||
return setAudioSettingsDropdownItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default AudioSettingsDropdownPluginStateContainer;
|
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const CameraSettingsDropdownPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
cameraSettingsDropdownItems,
|
||||
setCameraSettingsDropdownItems,
|
||||
] = useState<PluginSdk.CameraSettingsDropdownItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].cameraSettingsDropdownItems = cameraSettingsDropdownItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedCameraSettingsDropdownItems = (
|
||||
[] as PluginSdk.CameraSettingsDropdownItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.cameraSettingsDropdownItems),
|
||||
);
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
cameraSettingsDropdownItems: aggregatedCameraSettingsDropdownItems,
|
||||
},
|
||||
);
|
||||
}, [cameraSettingsDropdownItems]);
|
||||
|
||||
pluginApi.setCameraSettingsDropdownItems = (items: PluginSdk.CameraSettingsDropdownItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.CameraSettingsDropdownItem[];
|
||||
return setCameraSettingsDropdownItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default CameraSettingsDropdownPluginStateContainer;
|
@ -8,6 +8,12 @@ import {
|
||||
import PresentationToolbarPluginStateContainer from './presentation-toolbar/container';
|
||||
import UserListDropdownPluginStateContainer from './user-list-dropdown/container';
|
||||
import ActionButtonDropdownPluginStateContainer from './action-button-dropdown/container';
|
||||
import AudioSettingsDropdownPluginStateContainer from './audio-settings-dropdown/container';
|
||||
import ActionBarPluginStateContainer from './action-bar/container';
|
||||
import PresentationDropdownPluginStateContainer from './presentation-dropdown/container';
|
||||
import NavBarPluginStateContainer from './nav-bar/container';
|
||||
import OptionsDropdownPluginStateContainer from './options-dropdown/container';
|
||||
import CameraSettingsDropdownPluginStateContainer from './camera-settings-dropdown/container';
|
||||
import UserCameraDropdownPluginStateContainer from './user-camera-dropdown/container';
|
||||
|
||||
const pluginProvidedStateMap: PluginsProvidedStateMap = {};
|
||||
@ -16,6 +22,12 @@ const pluginProvidedStateContainers: PluginProvidedStateContainerChild[] = [
|
||||
PresentationToolbarPluginStateContainer,
|
||||
UserListDropdownPluginStateContainer,
|
||||
ActionButtonDropdownPluginStateContainer,
|
||||
AudioSettingsDropdownPluginStateContainer,
|
||||
ActionBarPluginStateContainer,
|
||||
PresentationDropdownPluginStateContainer,
|
||||
NavBarPluginStateContainer,
|
||||
OptionsDropdownPluginStateContainer,
|
||||
CameraSettingsDropdownPluginStateContainer,
|
||||
UserCameraDropdownPluginStateContainer,
|
||||
];
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const NavBarPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
navBarItems,
|
||||
setNavBarItems,
|
||||
] = useState<PluginSdk.NavBarItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].navBarItems = navBarItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedNavBarItems = ([] as PluginSdk.NavBarItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.navBarItems),
|
||||
);
|
||||
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
navBarItems: aggregatedNavBarItems,
|
||||
},
|
||||
);
|
||||
}, [navBarItems]);
|
||||
|
||||
pluginApi.setNavBarItems = (items: PluginSdk.NavBarItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.NavBarItem[];
|
||||
return setNavBarItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default NavBarPluginStateContainer;
|
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const OptionsDropdownPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
optionsDropdownItems,
|
||||
setOptionsDropdownItems,
|
||||
] = useState<PluginSdk.OptionsDropdownItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].optionsDropdownItems = optionsDropdownItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedOptionsDropdownItems = (
|
||||
[] as PluginSdk.OptionsDropdownItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.optionsDropdownItems),
|
||||
);
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
optionsDropdownItems: aggregatedOptionsDropdownItems,
|
||||
},
|
||||
);
|
||||
}, [optionsDropdownItems]);
|
||||
|
||||
pluginApi.setOptionsDropdownItems = (items: PluginSdk.OptionsDropdownItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.OptionsDropdownItem[];
|
||||
return setOptionsDropdownItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default OptionsDropdownPluginStateContainer;
|
@ -0,0 +1,54 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
import {
|
||||
PluginProvidedStateContainerChildProps, PluginProvidedState,
|
||||
PluginProvidedStateContainerChild,
|
||||
} from '../../types';
|
||||
import { PluginsContext } from '../../../components-data/plugin-context/context';
|
||||
|
||||
const PresentationDropdownPluginStateContainer = ((
|
||||
props: PluginProvidedStateContainerChildProps,
|
||||
) => {
|
||||
const {
|
||||
uuid,
|
||||
generateItemWithId,
|
||||
pluginProvidedStateMap,
|
||||
pluginApi,
|
||||
} = props;
|
||||
const [
|
||||
presentationDropdownItems,
|
||||
setPresentationDropdownItems,
|
||||
] = useState<PluginSdk.PresentationDropdownItem[]>([]);
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
setPluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Change this plugin provided toolbar items
|
||||
pluginProvidedStateMap[uuid].presentationDropdownItems = presentationDropdownItems;
|
||||
|
||||
// Update context with computed aggregated list of all plugin provided toolbar items
|
||||
const aggregatedPresentationDropdownItems = (
|
||||
[] as PluginSdk.PresentationDropdownItem[]).concat(
|
||||
...Object.values(pluginProvidedStateMap)
|
||||
.map((pps: PluginProvidedState) => pps.presentationDropdownItems),
|
||||
);
|
||||
setPluginsProvidedAggregatedState(
|
||||
{
|
||||
...pluginsProvidedAggregatedState,
|
||||
presentationDropdownItems: aggregatedPresentationDropdownItems,
|
||||
},
|
||||
);
|
||||
}, [presentationDropdownItems]);
|
||||
|
||||
pluginApi.setPresentationDropdownItems = (items: PluginSdk.PresentationDropdownItem[]) => {
|
||||
const itemsWithId = items.map(generateItemWithId) as PluginSdk.PresentationDropdownItem[];
|
||||
return setPresentationDropdownItems(itemsWithId);
|
||||
};
|
||||
return null;
|
||||
}) as PluginProvidedStateContainerChild;
|
||||
|
||||
export default PresentationDropdownPluginStateContainer;
|
@ -30,6 +30,12 @@ export interface PluginProvidedState {
|
||||
presentationToolbarItems: PluginSdk.PresentationToolbarItem[];
|
||||
userListDropdownItems: PluginSdk.UserListDropdownItem[];
|
||||
actionButtonDropdownItems: PluginSdk.ActionButtonDropdownItem[];
|
||||
audioSettingsDropdownItems: PluginSdk.AudioSettingsDropdownItem[];
|
||||
actionsBarItems: PluginSdk.ActionsBarItem[];
|
||||
presentationDropdownItems: PluginSdk.PresentationDropdownItem[];
|
||||
navBarItems: PluginSdk.NavBarItem[];
|
||||
optionsDropdownItems: PluginSdk.OptionsDropdownItem[];
|
||||
cameraSettingsDropdownItems: PluginSdk.CameraSettingsDropdownItem[];
|
||||
userCameraDropdownItems: PluginSdk.UserCameraDropdownItem[];
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import TooltipContainer from '/imports/ui/components/common/tooltip/container';
|
||||
import { ACTIONS } from '/imports/ui/components/layout/enums';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import AppService from '/imports/ui/components/app/service';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
downloading: {
|
||||
@ -43,7 +44,7 @@ const intlMessages = defineMessages({
|
||||
defaultMessage: 'Minimize',
|
||||
},
|
||||
optionsLabel: {
|
||||
id: 'app.navBar.settingsDropdown.optionsLabel',
|
||||
id: 'app.navBar.optionsDropdown.optionsLabel',
|
||||
description: 'Options button label',
|
||||
defaultMessage: 'Options',
|
||||
},
|
||||
@ -88,6 +89,10 @@ const propTypes = {
|
||||
getShapes: PropTypes.func.isRequired,
|
||||
currentPageId: PropTypes.string.isRequired,
|
||||
}),
|
||||
presentationDropdownItems: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -124,6 +129,7 @@ const PresentationMenu = (props) => {
|
||||
isToolbarVisible,
|
||||
setIsToolbarVisible,
|
||||
allowSnapshotOfCurrentSlide,
|
||||
presentationDropdownItems,
|
||||
} = props;
|
||||
|
||||
const [state, setState] = useState({
|
||||
@ -298,6 +304,27 @@ const PresentationMenu = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
presentationDropdownItems.forEach((item, index) => {
|
||||
switch (item.type) {
|
||||
case PluginSdk.PresentationDropdownItemType.OPTION:
|
||||
menuItems.push({
|
||||
key: `${item.id}-${index}`,
|
||||
label: item.label,
|
||||
icon: item.icon,
|
||||
onClick: item.onClick,
|
||||
});
|
||||
break;
|
||||
case PluginSdk.PresentationDropdownItemType.SEPARATOR:
|
||||
menuItems.push({
|
||||
key: `${item.id}-${index}`,
|
||||
isSeparator: true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import PresentationMenu from './component';
|
||||
@ -9,6 +10,7 @@ import { layoutSelect, layoutDispatch } from '/imports/ui/components/layout/cont
|
||||
import WhiteboardService from '/imports/ui/components/whiteboard/service';
|
||||
import UserService from '/imports/ui/components/user-list/service';
|
||||
import { isSnapshotOfCurrentSlideEnabled } from '/imports/ui/services/features';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
const PresentationMenuContainer = (props) => {
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
@ -17,6 +19,13 @@ const PresentationMenuContainer = (props) => {
|
||||
const { elementId } = props;
|
||||
const isFullscreen = currentElement === elementId;
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const { pluginsProvidedAggregatedState } = useContext(PluginsContext);
|
||||
let presentationDropdownItems = [];
|
||||
if (pluginsProvidedAggregatedState.presentationDropdownItems) {
|
||||
presentationDropdownItems = [
|
||||
...pluginsProvidedAggregatedState.presentationDropdownItems,
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<PresentationMenu
|
||||
@ -27,6 +36,7 @@ const PresentationMenuContainer = (props) => {
|
||||
isFullscreen,
|
||||
layoutContextDispatch,
|
||||
isRTL,
|
||||
presentationDropdownItems,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -10,6 +10,7 @@ import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||
import { isVirtualBackgroundsEnabled } from '/imports/ui/services/features';
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
import VideoPreviewContainer from '/imports/ui/components/video-preview/container';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import Settings from '/imports/ui/services/settings';
|
||||
|
||||
const ENABLE_WEBCAM_SELECTOR_BUTTON = Meteor.settings.public.app.enableWebcamSelectorButton;
|
||||
@ -60,6 +61,10 @@ const propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasVideoStream: PropTypes.bool.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
cameraSettingsDropdownItems: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
const JoinVideoButton = ({
|
||||
@ -68,6 +73,7 @@ const JoinVideoButton = ({
|
||||
status,
|
||||
disableReason,
|
||||
updateSettings,
|
||||
cameraSettingsDropdownItems,
|
||||
}) => {
|
||||
const { isMobile } = deviceInfo;
|
||||
const isMobileSharingCamera = hasVideoStream && isMobile;
|
||||
@ -163,6 +169,26 @@ const JoinVideoButton = ({
|
||||
if (actions.length === 0) return null;
|
||||
const customStyles = { top: '-3.6rem' };
|
||||
|
||||
cameraSettingsDropdownItems.forEach((plugin) => {
|
||||
switch (plugin.type) {
|
||||
case PluginSdk.CameraSettingsDropdownItemType.OPTION:
|
||||
actions.push({
|
||||
key: plugin.id,
|
||||
label: plugin.label,
|
||||
onClick: plugin.onClick,
|
||||
icon: plugin.icon,
|
||||
});
|
||||
break;
|
||||
case PluginSdk.CameraSettingsDropdownItemType.SEPARATOR:
|
||||
actions.push({
|
||||
key: plugin.id,
|
||||
isSeparator: true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<BBBMenu
|
||||
customStyles={!isMobile ? customStyles : null}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import JoinVideoButton from './component';
|
||||
@ -6,6 +7,7 @@ import VideoService from '../service';
|
||||
import {
|
||||
updateSettings,
|
||||
} from '/imports/ui/components/settings/service';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
const JoinVideoOptionsContainer = (props) => {
|
||||
const {
|
||||
@ -17,9 +19,23 @@ const JoinVideoOptionsContainer = (props) => {
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const {
|
||||
pluginsProvidedAggregatedState,
|
||||
} = useContext(PluginsContext);
|
||||
let cameraSettingsDropdownItems = [];
|
||||
if (pluginsProvidedAggregatedState.cameraSettingsDropdownItems) {
|
||||
cameraSettingsDropdownItems = [
|
||||
...pluginsProvidedAggregatedState.cameraSettingsDropdownItems,
|
||||
];
|
||||
}
|
||||
return (
|
||||
<JoinVideoButton {...{
|
||||
hasVideoStream, updateSettings, disableReason, status, ...restProps,
|
||||
cameraSettingsDropdownItems,
|
||||
hasVideoStream,
|
||||
updateSettings,
|
||||
disableReason,
|
||||
status,
|
||||
...restProps,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
6
bigbluebutton-html5/package-lock.json
generated
6
bigbluebutton-html5/package-lock.json
generated
@ -3754,9 +3754,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"bigbluebutton-html-plugin-sdk": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.7.tgz",
|
||||
"integrity": "sha512-9bCJwVGJoEhhwCNP/N1d/loSLsK6DgWsc6zcwqPk6mOzSlNeiJ0+uzhg4sB44JYN2vr6S7+BrhPF5rBwrT3ExA=="
|
||||
"version": "0.0.14",
|
||||
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.14.tgz",
|
||||
"integrity": "sha512-Yz1+E36U24949zjmevT9Kthg1d4iog5k1M2qPHs/ovIbTed+T24StmmxC8AX/8ZmwKUSCZvWmT5gM7qOXN1X+w=="
|
||||
},
|
||||
"bintrees": {
|
||||
"version": "1.0.2",
|
||||
|
@ -45,7 +45,7 @@
|
||||
"autoprefixer": "^10.4.4",
|
||||
"axios": "^0.21.3",
|
||||
"babel-runtime": "~6.26.0",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.7",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.14",
|
||||
"bowser": "^2.11.0",
|
||||
"browser-bunyan": "^1.8.0",
|
||||
"classnames": "^2.2.6",
|
||||
|
@ -430,24 +430,24 @@
|
||||
"app.muteWarning.label": "Click {0} to unmute yourself.",
|
||||
"app.muteWarning.disableMessage": "Mute alerts disabled until unmute",
|
||||
"app.muteWarning.tooltip": "Click to close and disable warning until next unmute",
|
||||
"app.navBar.settingsDropdown.optionsLabel": "Options",
|
||||
"app.navBar.settingsDropdown.fullscreenLabel": "Fullscreen Application",
|
||||
"app.navBar.settingsDropdown.settingsLabel": "Settings",
|
||||
"app.navBar.settingsDropdown.aboutLabel": "About",
|
||||
"app.navBar.settingsDropdown.leaveSessionLabel": "Leave meeting",
|
||||
"app.navBar.settingsDropdown.exitFullscreenLabel": "Exit Fullscreen",
|
||||
"app.navBar.settingsDropdown.fullscreenDesc": "Make the settings menu fullscreen",
|
||||
"app.navBar.settingsDropdown.settingsDesc": "Change the general settings",
|
||||
"app.navBar.settingsDropdown.aboutDesc": "Show information about the client",
|
||||
"app.navBar.settingsDropdown.leaveSessionDesc": "Leave the meeting",
|
||||
"app.navBar.settingsDropdown.exitFullscreenDesc": "Exit fullscreen mode",
|
||||
"app.navBar.settingsDropdown.hotkeysLabel": "Keyboard shortcuts",
|
||||
"app.navBar.settingsDropdown.hotkeysDesc": "Listing of available keyboard shortcuts",
|
||||
"app.navBar.settingsDropdown.helpLabel": "Help",
|
||||
"app.navBar.settingsDropdown.openAppLabel": "Open in BigBlueButton Tablet app",
|
||||
"app.navBar.settingsDropdown.helpDesc": "Links user to video tutorials (opens new tab)",
|
||||
"app.navBar.settingsDropdown.endMeetingDesc": "Terminates the current meeting",
|
||||
"app.navBar.settingsDropdown.endMeetingLabel": "End meeting",
|
||||
"app.navBar.optionsDropdown.optionsLabel": "Options",
|
||||
"app.navBar.optionsDropdown.fullscreenLabel": "Fullscreen Application",
|
||||
"app.navBar.optionsDropdown.settingsLabel": "Settings",
|
||||
"app.navBar.optionsDropdown.aboutLabel": "About",
|
||||
"app.navBar.optionsDropdown.leaveSessionLabel": "Leave meeting",
|
||||
"app.navBar.optionsDropdown.exitFullscreenLabel": "Exit Fullscreen",
|
||||
"app.navBar.optionsDropdown.fullscreenDesc": "Make the settings menu fullscreen",
|
||||
"app.navBar.optionsDropdown.settingsDesc": "Change the general settings",
|
||||
"app.navBar.optionsDropdown.aboutDesc": "Show information about the client",
|
||||
"app.navBar.optionsDropdown.leaveSessionDesc": "Leave the meeting",
|
||||
"app.navBar.optionsDropdown.exitFullscreenDesc": "Exit fullscreen mode",
|
||||
"app.navBar.optionsDropdown.hotkeysLabel": "Keyboard shortcuts",
|
||||
"app.navBar.optionsDropdown.hotkeysDesc": "Listing of available keyboard shortcuts",
|
||||
"app.navBar.optionsDropdown.helpLabel": "Help",
|
||||
"app.navBar.optionsDropdown.openAppLabel": "Open in BigBlueButton Tablet app",
|
||||
"app.navBar.optionsDropdown.helpDesc": "Links user to video tutorials (opens new tab)",
|
||||
"app.navBar.optionsDropdown.endMeetingDesc": "Terminates the current meeting",
|
||||
"app.navBar.optionsDropdown.endMeetingLabel": "End meeting",
|
||||
"app.navBar.userListToggleBtnLabel": "User list toggle",
|
||||
"app.navBar.toggleUserList.ariaLabel": "Users and messages toggle",
|
||||
"app.navBar.toggleUserList.newMessages": "with new message notification",
|
||||
|
Loading…
Reference in New Issue
Block a user