Merge pull request #18825 from GuiLeme/plugin-sdk-navbar

feat(plugin): extensible area navigation bar (above presentation on the presentation area)
This commit is contained in:
Ramón Souza 2023-09-25 20:34:26 -03:00 committed by GitHub
commit 3b531125dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 198 additions and 5 deletions

View File

@ -2,6 +2,7 @@ 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';
@ -13,6 +14,7 @@ import TimerIndicatorContainer from '/imports/ui/components/timer/indicator/cont
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,8 +340,10 @@ class NavBar extends Component {
amIModerator={amIModerator}
currentUserId={currentUserId}
/>
{renderPluginItems(centerPluginItems)}
</Styled.Center>
<Styled.Right>
{renderPluginItems(rightPluginItems)}
{ConnectionStatusService.isEnabled() ? <ConnectionStatusButton /> : null}
<SettingsDropdownContainer amIModerator={amIModerator} />
</Styled.Right>

View File

@ -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);

View File

@ -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,
};

View File

@ -11,6 +11,7 @@ import ActionButtonDropdownPluginStateContainer from './action-button-dropdown/c
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';
const pluginProvidedStateMap: PluginsProvidedStateMap = {};
@ -21,6 +22,7 @@ const pluginProvidedStateContainers: PluginProvidedStateContainerChild[] = [
AudioSettingsDropdownPluginStateContainer,
ActionBarPluginStateContainer,
PresentationDropdownPluginStateContainer,
NavBarPluginStateContainer,
];
function generateItemWithId<T extends PluginSdk.PluginProvidedUiItemDescriptor>(

View File

@ -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;

View File

@ -33,6 +33,7 @@ export interface PluginProvidedState {
audioSettingsDropdownItems: PluginSdk.AudioSettingsDropdownItem[];
actionsBarItems: PluginSdk.ActionsBarItem[];
presentationDropdownItems: PluginSdk.PresentationDropdownItem[];
navBarItems: PluginSdk.NavBarItem[];
}
/**

View File

@ -3754,9 +3754,9 @@
"dev": true
},
"bigbluebutton-html-plugin-sdk": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.11.tgz",
"integrity": "sha512-mpZ6a7f5mhCSzdJbWZYUgoF+dj6598BidshBOMBFhZEzZjLUEGHkIjoac6yUHnjYOOLXcIttwYC07PwQlK8qDQ=="
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.12.tgz",
"integrity": "sha512-w22ghyDfIYAVXRgu2GnrSjLZicouNOTK1v9Lwy5o7EpoR3YSLsTV9e7h7zmQuz5M4cgwFcs7KcllIcg83poYrg=="
},
"bintrees": {
"version": "1.0.2",

View File

@ -45,7 +45,7 @@
"autoprefixer": "^10.4.4",
"axios": "^0.21.3",
"babel-runtime": "~6.26.0",
"bigbluebutton-html-plugin-sdk": "0.0.11",
"bigbluebutton-html-plugin-sdk": "0.0.12",
"bowser": "^2.11.0",
"browser-bunyan": "^1.8.0",
"classnames": "^2.2.6",