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