diff --git a/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/component.tsx b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/component.tsx new file mode 100644 index 0000000000..1bc78e6dcf --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/component.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import Styled from './styles'; +import { GenericSidekickContentProps } from '../types'; +import GenericContentItem from '../generic-content-item/component'; +import { layoutDispatch } from '/imports/ui/components/layout/context'; +import { PANELS, ACTIONS } from '/imports/ui/components/layout/enums'; + + +const GenericSidekickContent: React.FC = ({ + renderFunction, + genericContentId, + genericContentLabel, +}) => { + const layoutContextDispatch = layoutDispatch(); + return ( + + { + layoutContextDispatch({ + type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN, + value: false, + }); + layoutContextDispatch({ + type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL, + value: PANELS.NONE, + }); + }, + 'data-test': `hide_${genericContentId}`, + 'aria-label': genericContentLabel, + label: genericContentLabel, + }} + customRightButton={null} + /> + + + ); +}; + +export default GenericSidekickContent; \ No newline at end of file diff --git a/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/container.tsx b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/container.tsx new file mode 100644 index 0000000000..05279711d7 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/container.tsx @@ -0,0 +1,47 @@ +import React, { useContext } from 'react'; +import * as PluginSdk from 'bigbluebutton-html-plugin-sdk'; +import { GenericContentType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/generic-content/enums'; +import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context'; +import { PANELS } from '/imports/ui/components/layout/enums'; +import GenericSidekickContent from './component'; +import { GenericSidekickContentContainerProps } from '../types'; +import logger from '/imports/startup/client/logger'; + + +const GenericSidekickContentContainer: React.FC = (props: GenericSidekickContentContainerProps) => { + const { genericSidekickContentId } = props; + const genericSidekickContentIdIsolated = genericSidekickContentId.replace(PANELS.GENERIC_SIDEKICK_CONTENT, ""); + + const { + pluginsExtensibleAreasAggregatedState, + } = useContext(PluginsContext); + let genericSidekickContentExtensibleArea = [] as PluginSdk.GenericSidekickContent[]; + + if (pluginsExtensibleAreasAggregatedState.genericContents) { + const genericMainContent = pluginsExtensibleAreasAggregatedState.genericContents.filter((g) => g.type === GenericContentType.SIDEKICK_CONTENT) as PluginSdk.GenericSidekickContent[]; + genericSidekickContentExtensibleArea = [...genericMainContent]; + } + + if (genericSidekickContentExtensibleArea.length === 0) return null; + const pickedGenericSidekickContent = genericSidekickContentExtensibleArea.filter((gsc) => gsc.id === genericSidekickContentIdIsolated)[0]; + + if (!pickedGenericSidekickContent) { + logger.error({ + logCode: 'generic_sidekick_content_not_found', + extraInfo: { + genericSidekickContentId, + genericSidekickContentIdIsolated, + }, + }, `Generic sidekick content with id ${genericSidekickContentIdIsolated} not found`); + } + + return ( + + ); +}; + +export default GenericSidekickContentContainer; diff --git a/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/styles.ts b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/styles.ts new file mode 100644 index 0000000000..437c0ed4c2 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/generic-content/generic-sidekick-content/styles.ts @@ -0,0 +1,33 @@ +import styled from 'styled-components'; +import { + mdPaddingX, +} from '/imports/ui/stylesheets/styled-components/general'; +import { colorWhite } from '/imports/ui/stylesheets/styled-components/palette'; +import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints'; +import CommonHeader from '/imports/ui/components/common/control-header/component'; + +const Container = styled.div` + background-color: ${colorWhite}; + padding: ${mdPaddingX}; + display: flex; + flex-grow: 1; + flex-direction: column; + overflow: hidden; + height: 100%; + + @media ${smallOnly} { + transform: none !important; + &.no-padding { + padding: 0; + } + } +`; + +const Header = styled(CommonHeader)` + padding-bottom: .2rem; +`; + +export default { + Container, + Header, +}; diff --git a/bigbluebutton-html5/imports/ui/components/generic-content/types.ts b/bigbluebutton-html5/imports/ui/components/generic-content/types.ts index a29ea4ebe4..fa72762125 100644 --- a/bigbluebutton-html5/imports/ui/components/generic-content/types.ts +++ b/bigbluebutton-html5/imports/ui/components/generic-content/types.ts @@ -12,3 +12,12 @@ export interface GenericMainContentProps { renderFunctionComponents: GenericMainContent[]; } +export interface GenericSidekickContentContainerProps { + genericSidekickContentId: string; +} + +export interface GenericSidekickContentProps { + genericContentId: string; + renderFunction: (element: HTMLElement) => void; + genericContentLabel: string; +} diff --git a/bigbluebutton-html5/imports/ui/components/layout/enums.js b/bigbluebutton-html5/imports/ui/components/layout/enums.js index ca0a1b8eee..3968aee716 100644 --- a/bigbluebutton-html5/imports/ui/components/layout/enums.js +++ b/bigbluebutton-html5/imports/ui/components/layout/enums.js @@ -133,5 +133,6 @@ export const PANELS = { SHARED_NOTES: 'shared-notes', TIMER: 'timer', WAITING_USERS: 'waiting-users', + GENERIC_SIDEKICK_CONTENT: 'generic-sidekick-content', NONE: 'none', }; diff --git a/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx b/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx index e23e57bc8c..7d63803e57 100644 --- a/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import Resizable from 're-resizable'; +import { GenericContentType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/generic-content/enums'; import { ACTIONS, PANELS } from '../layout/enums'; import ChatContainer from '/imports/ui/components/chat/chat-graphql/component'; import NotesContainer from '/imports/ui/components/notes/component'; @@ -11,6 +12,7 @@ import GuestUsersManagementPanel from '/imports/ui/components/waiting-users/wait import Styled from './styles'; import ErrorBoundary from '/imports/ui/components/common/error-boundary/component'; import FallbackView from '/imports/ui/components/common/fallback-errors/fallback-view/component'; +import GenericSidekickContentContainer from '/imports/ui/components/generic-content/generic-sidekick-content/container'; const propTypes = { top: PropTypes.number.isRequired, @@ -157,6 +159,11 @@ const SidebarContent = (props) => { /> )} + {sidebarContentPanel.includes(PANELS.GENERIC_SIDEKICK_CONTENT) && ( + + )} ); }; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx index 2dce1a8a52..7eedc69c5e 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx @@ -10,6 +10,8 @@ import UserPollsContainer from './user-polls/container'; import BreakoutRoomContainer from './breakout-room/container'; import { isChatEnabled } from '/imports/ui/services/features'; import UserTitleContainer from '../user-list-graphql/user-participants-title/component'; +import { GenericSidekickContent } from 'bigbluebutton-html-plugin-sdk'; +import GenericSidekickContentNavButtonContainer from './generic-sidekick-content-button/container'; const propTypes = { currentUser: PropTypes.shape({ @@ -46,9 +48,10 @@ class UserContent extends PureComponent { {isTimerActive && } {currentUser?.role === ROLE_MODERATOR ? ( - ) : null} + ) : null} + diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/component.jsx new file mode 100644 index 0000000000..40a995af42 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/component.jsx @@ -0,0 +1,59 @@ +import React, { useContext } from 'react'; +import PluginSdk from 'bigbluebutton-html-plugin-sdk'; +import { GenericContentType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/generic-content/enums'; +import Icon from '/imports/ui/components/common/icon/component'; +import Styled from './styles'; +import { ACTIONS, PANELS } from '/imports/ui/components/layout/enums'; +import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context'; + +const GenericSidekickContentNavButton = ({ sidebarContentPanel, layoutContextDispatch }) => { + const { pluginsExtensibleAreasAggregatedState } = useContext(PluginsContext); + let genericSidekickContentExtensibleArea = []; + + if (pluginsExtensibleAreasAggregatedState.genericContents) { + const genericMainContent = pluginsExtensibleAreasAggregatedState.genericContents.filter((g) => g.type === GenericContentType.SIDEKICK_CONTENT); + genericSidekickContentExtensibleArea = [...genericMainContent]; + } + + const genericSidekickContentId = (id) => PANELS.GENERIC_SIDEKICK_CONTENT + id; + + if (genericSidekickContentExtensibleArea.length === 0) return null; + const genericSidekickContentNavButtons = genericSidekickContentExtensibleArea.map((gsc) => ( + + + + {gsc.section} + + + + + { + layoutContextDispatch({ + type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN, + value: sidebarContentPanel !== genericSidekickContentId(gsc.id), + }); + layoutContextDispatch({ + type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL, + value: sidebarContentPanel === genericSidekickContentId(gsc.id) + ? PANELS.NONE + : genericSidekickContentId(gsc.id), + }); + }} + > + + + {gsc.name} + + + + + + )); + return <>{genericSidekickContentNavButtons} +} + +export default GenericSidekickContentNavButton; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/container.jsx new file mode 100644 index 0000000000..3d808b55d7 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/container.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import GenericSidekickContentNavButton from './component'; +import { layoutSelectInput, layoutDispatch } from '/imports/ui/components/layout/context'; + +const GenericSidekickContentNavButtonContainer = (props) => { + const sidebarContent = layoutSelectInput((i) => i.sidebarContent); + const { sidebarContentPanel } = sidebarContent; + const layoutContextDispatch = layoutDispatch(); + + return ( + + ); +}; + +export default GenericSidekickContentNavButtonContainer; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/styles.js b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/styles.js new file mode 100644 index 0000000000..d80548def1 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/generic-sidekick-content-button/styles.js @@ -0,0 +1,27 @@ +import styled from 'styled-components'; + +import Styled from '/imports/ui/components/user-list/styles'; +import StyledContent from '/imports/ui/components/user-list/user-list-content/styles'; + +const ListItem = styled(StyledContent.ListItem)` + i{ left: 4px; } +`; + +const Section = styled(Styled.Messages)``; + +const Container = styled(StyledContent.Container)``; + +const SmallTitle = styled(Styled.SmallTitle)``; + +const ScrollableList = styled(StyledContent.ScrollableList)``; + +const List = styled(StyledContent.List)``; + +export default { + ListItem, + Section, + Container, + SmallTitle, + ScrollableList, + List, +};