[plugin-server-commands] - added server-command caption save and add a captionLocale

This commit is contained in:
Guilherme Leme 2024-06-18 09:20:35 -03:00
parent 4063ee811b
commit 8db3a6be80
26 changed files with 384 additions and 110 deletions

View File

@ -82,7 +82,7 @@ trait UpdateTranscriptPubMsgHdlr {
for {
u <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield {
CaptionDAO.insertOrUpdateAudioCaption(msg.body.transcriptId, meetingId, msg.header.userId, transcript, u.speechLocale)
CaptionDAO.insertOrUpdateCaption(msg.body.transcriptId, meetingId, msg.header.userId, transcript, u.speechLocale)
}
broadcastEvent(

View File

@ -6,7 +6,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.db.{ CaptionLocaleDAO, CaptionTypes }
import org.bigbluebutton.core.db.{ CaptionDAO, CaptionLocaleDAO, CaptionTypes }
class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementTrait {
val log = Logging(context.system, getClass)
@ -51,7 +51,23 @@ class CaptionApp2x(implicit val context: ActorContext) extends RightsManagementT
}
}
}
def handle(msg: CaptionSubmitTranscriptPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
val meetingId = liveMeeting.props.meetingProp.intId
def broadcastSuccessEvent(transcriptId: String, transcript: String, locale: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(CaptionSubmitTranscriptSuccessEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(CaptionSubmitTranscriptSuccessEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = CaptionSubmitTranscriptSuccessEvtMsgBody(transcriptId, transcript, locale, msg.body.captionType)
val event = CaptionSubmitTranscriptSuccessEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
CaptionDAO.insertOrUpdateCaption(msg.body.transcriptId, meetingId, msg.header.userId,
msg.body.transcript, msg.body.locale, msg.body.captionType)
broadcastSuccessEvent(msg.body.transcriptId, msg.body.transcript, msg.body.locale)
}
def handle(msg: SendCaptionHistoryReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: SendCaptionHistoryReqMsg, history: Map[String, TranscriptVO]): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, msg.header.userId)

View File

@ -32,13 +32,14 @@ object CaptionTypes {
object CaptionDAO {
def insertOrUpdateAudioCaption(captionId: String, meetingId: String, userId: String, transcript: String, locale: String) = {
def insertOrUpdateCaption(captionId: String, meetingId: String, userId: String, transcript: String,
locale: String, captionType: String = CaptionTypes.AUDIO_TRANSCRIPTION) = {
DatabaseConnection.db.run(
TableQuery[CaptionTableDef].insertOrUpdate(
CaptionDbModel(
captionId = captionId,
meetingId = meetingId,
captionType = CaptionTypes.AUDIO_TRANSCRIPTION,
captionType = captionType,
userId = userId,
locale = locale,
captionText = transcript,

View File

@ -347,6 +347,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[AddCaptionLocalePubMsg](envelope, jsonNode)
case SendCaptionHistoryReqMsg.NAME =>
routeGenericMsg[SendCaptionHistoryReqMsg](envelope, jsonNode)
case CaptionSubmitTranscriptPubMsg.NAME =>
routeGenericMsg[CaptionSubmitTranscriptPubMsg](envelope, jsonNode)
// Chat
case GetChatHistoryReqMsg.NAME =>

View File

@ -654,6 +654,7 @@ class MeetingActor(
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: AddCaptionLocalePubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: CaptionSubmitTranscriptPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
// Guests
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)

View File

@ -9,6 +9,22 @@ object AddCaptionLocalePubMsg { val NAME = "AddCaptionLocalePubMsg" }
case class AddCaptionLocalePubMsg(header: BbbClientMsgHeader, body: AddCaptionLocalePubMsgBody) extends StandardMsg
case class AddCaptionLocalePubMsgBody(locale: String)
object CaptionSubmitTranscriptPubMsg { val NAME = "CaptionSubmitTranscriptPubMsg" }
case class CaptionSubmitTranscriptPubMsg(header: BbbClientMsgHeader, body: CaptionSubmitTranscriptPubMsgBody) extends StandardMsg
case class CaptionSubmitTranscriptPubMsgBody(
transcriptId: String,
transcript: String,
locale: String,
captionType: String,
)
object CaptionSubmitTranscriptSuccessEvtMsg { val NAME = "CaptionSubmitTranscriptSuccessEvtMsg" }
case class CaptionSubmitTranscriptSuccessEvtMsg(header: BbbClientMsgHeader, body: CaptionSubmitTranscriptSuccessEvtMsgBody) extends StandardMsg
case class CaptionSubmitTranscriptSuccessEvtMsgBody(
transcriptId: String,
transcript: String,
locale: String,
captionType: String,
)
object SendCaptionHistoryReqMsg { val NAME = "SendCaptionHistoryReqMsg" }
case class SendCaptionHistoryReqMsg(header: BbbClientMsgHeader, body: SendCaptionHistoryReqMsgBody) extends StandardMsg
case class SendCaptionHistoryReqMsgBody()

View File

@ -0,0 +1,36 @@
import { throwErrorIfInvalidInput } from '../imports/validation';
import { RedisMessage } from '../types';
export default function buildRedisMessage(sessionVariables: Record<string, unknown>, input: Record<string, unknown>): RedisMessage {
const eventName = `CaptionSubmitTranscriptPubMsg`;
throwErrorIfInvalidInput(input,
[
{name: 'transcriptId', type: 'string', required: true},
{name: 'transcript', type: 'string', required: true},
{name: 'locale', type: 'string', required: true},
{name: 'captionType', type: 'string', required: true},
]
)
const routing = {
meetingId: sessionVariables['x-hasura-meetingid'] as String,
userId: sessionVariables['x-hasura-userid'] as String
};
const header = {
name: eventName,
meetingId: routing.meetingId,
userId: routing.userId
};
const body = {
transcriptId: input.transcriptId,
transcript: input.transcript,
locale: input.locale,
captionType: input.captionType,
};
return { eventName, routing, header, body };
}

View File

@ -11,7 +11,7 @@ export default function buildRedisMessage(sessionVariables: Record<string, unkno
]
)
const eventName = `PluginDataChannelDeleteMessageMsg`;
const eventName = `PluginDataChannelDeleteEntryMsg`;
const routing = {
meetingId: sessionVariables['x-hasura-meetingid'] as String,

View File

@ -77,6 +77,14 @@ type Mutation {
): Boolean
}
type Mutation {
captionSubmitTranscript(
transcriptId: String!
transcript: String!
locale: String!
): Boolean
}
type Mutation {
chatCreateWithUser(
userId: String!

View File

@ -71,6 +71,12 @@ actions:
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
permissions:
- role: bbb_client
- name: captionSubmitTranscript
definition:
kind: synchronous
handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}'
permissions:
- role: bbb_client
- name: chatCreateWithUser
definition:
kind: synchronous

View File

@ -16,6 +16,7 @@ const GenericContentItem: React.FC<GenericContentItemProps> = (props) => {
return (
<div
style={{
height: '100%',
overflow: 'hidden',
}}
ref={elementRef}

View File

@ -2,43 +2,40 @@ 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<GenericSidekickContentProps> = ({
renderFunction,
layoutContextDispatch,
genericContentId,
genericContentLabel,
}) => {
const layoutContextDispatch = layoutDispatch();
return (
<Styled.Container
data-test={genericContentId}
>
<Styled.Header
leftButtonProps={{
onClick: () => {
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}
/>
<GenericContentItem
key={genericContentId}
renderFunction={renderFunction}
/>
</Styled.Container>
);
};
}) => (
<Styled.Container
data-test={genericContentId}
>
<Styled.Header
leftButtonProps={{
onClick: () => {
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}
/>
<GenericContentItem
key={genericContentId}
renderFunction={renderFunction}
/>
</Styled.Container>
);
export default GenericSidekickContent;

View File

@ -2,30 +2,42 @@ 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-item/enums';
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
import { PANELS } from '/imports/ui/components/layout/enums';
import { PANELS, ACTIONS } from '/imports/ui/components/layout/enums';
import GenericSidekickContent from './component';
import { GenericContentSidekickContainerProps } from '../types';
import logger from '/imports/startup/client/logger';
import { layoutDispatch } from '../../layout/context';
const GenericContentSidekickContainer: React.FC<GenericContentSidekickContainerProps> = (
props: GenericContentSidekickContainerProps,
) => {
const { genericSidekickContentId } = props;
const genericSidekickContentIdIsolated = genericSidekickContentId.replace(PANELS.GENERIC_CONTENT_SIDEKICK, '');
const layoutContextDispatch = layoutDispatch();
const {
pluginsExtensibleAreasAggregatedState,
} = useContext(PluginsContext);
let genericSidekickContentExtensibleArea = [] as PluginSdk.GenericContentSidekickArea[];
let genericContentSidekickAreaExtensibleArea = [] as PluginSdk.GenericContentSidekickArea[];
if (pluginsExtensibleAreasAggregatedState.genericContentItems) {
const genericMainContent = pluginsExtensibleAreasAggregatedState.genericContentItems
const genericContentSidekickArea = pluginsExtensibleAreasAggregatedState.genericContentItems
.filter((g) => g.type === GenericContentType.SIDEKICK_AREA) as PluginSdk.GenericContentSidekickArea[];
genericSidekickContentExtensibleArea = [...genericMainContent];
genericContentSidekickAreaExtensibleArea = [...genericContentSidekickArea];
}
if (genericSidekickContentExtensibleArea.length === 0) return null;
const pickedGenericSidekickContent = genericSidekickContentExtensibleArea
if (genericContentSidekickAreaExtensibleArea.length === 0) {
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
value: false,
});
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
value: PANELS.NONE,
});
return null;
}
const pickedGenericSidekickContent = genericContentSidekickAreaExtensibleArea
.filter((gsc) => gsc.id === genericSidekickContentIdIsolated)[0];
if (!pickedGenericSidekickContent) {
@ -40,6 +52,7 @@ const GenericContentSidekickContainer: React.FC<GenericContentSidekickContainerP
return (
<GenericSidekickContent
layoutContextDispatch={layoutContextDispatch}
genericContentId={pickedGenericSidekickContent.id}
renderFunction={pickedGenericSidekickContent.contentFunction}
genericContentLabel={pickedGenericSidekickContent.name}

View File

@ -17,6 +17,7 @@ export interface GenericContentSidekickContainerProps {
}
export interface GenericSidekickContentProps {
layoutContextDispatch: (...args: unknown[]) => void;
genericContentId: string;
renderFunction: (element: HTMLElement) => void;
genericContentLabel: string;

View File

@ -97,7 +97,7 @@ const DataChannelItemManagerWriter: React.ElementType<DataChannelItemManagerWrit
variables: {
pluginName: hookArguments?.pluginName,
channelName: hookArguments?.channelName,
messageId: eventDetails.data,
entryId: eventDetails.data,
subChannelName,
},
});

View File

@ -1,7 +1,7 @@
import { gql } from '@apollo/client';
const PLUGIN_DATA_CHANNEL_NEW_ITEMS = gql`
subscription FetchPluginDataChannelEntryMsg($pluginName: String!,
subscription FetchPluginDataChannelEntry($pluginName: String!,
$channelName: String! , $createdAt: timestamptz!, $subChannelName: String!){
pluginDataChannelEntry_stream(
cursor: {initial_value: {createdAt: $createdAt}}, batch_size: 100,
@ -24,7 +24,7 @@ const PLUGIN_DATA_CHANNEL_NEW_ITEMS = gql`
`;
const PLUGIN_DATA_CHANNEL_All_ITEMS = gql`
subscription FetchPluginDataChannelEntryMsg($pluginName: String!,
subscription FetchPluginDataChannelEntry($pluginName: String!,
$channelName: String!, $subChannelName: String!
){
pluginDataChannelEntry(
@ -48,7 +48,7 @@ const PLUGIN_DATA_CHANNEL_All_ITEMS = gql`
`;
const PLUGIN_DATA_CHANNEL_LATEST_ITEM = gql`
subscription FetchPluginDataChannelEntryMsg($pluginName: String!,
subscription FetchPluginDataChannelEntry($pluginName: String!,
$channelName: String!, $subChannelName: String!
){
pluginDataChannelEntry(

View File

@ -15,6 +15,7 @@ import ExtensibleAreaStateManager from './extensible-areas/manager';
import PluginDataChannelManager from './data-channel/manager';
import PluginUiCommandsHandler from './ui-commands/handler';
import PluginDomElementManipulationManager from './dom-element-manipulation/manager';
import PluginServerCommandsHandler from './server-commands/handler';
const PluginsEngineManager = () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -53,6 +54,7 @@ const PluginsEngineManager = () => {
}}
/>
<PluginDataConsumptionManager />
<PluginServerCommandsHandler />
<PluginUiCommandsHandler />
<PluginDomElementManipulationManager />
{

View File

@ -0,0 +1,30 @@
import { useEffect } from 'react';
import { useMutation } from '@apollo/client';
import {
CaptionCommandsEnum,
} from 'bigbluebutton-html-plugin-sdk/dist/cjs/server-commands/caption/enum';
import CAPTION_ADD_LOCALE from './mutations';
const PluginAddLocaleCaptionServerCommandsManager = () => {
const [captionAddLocale] = useMutation(CAPTION_ADD_LOCALE);
const handleCaptionAddLocale = ((
event: CustomEvent<string>,
) => {
captionAddLocale({
variables: {
locale: event.detail,
},
});
}) as EventListener;
useEffect(() => {
window.addEventListener(CaptionCommandsEnum.ADD_LOCALE, handleCaptionAddLocale);
return () => {
window.removeEventListener(CaptionCommandsEnum.ADD_LOCALE, handleCaptionAddLocale);
};
}, []);
return null;
};
export default PluginAddLocaleCaptionServerCommandsManager;

View File

@ -8,6 +8,4 @@ export const CAPTION_ADD_LOCALE = gql`
}
`;
export default {
CAPTION_ADD_LOCALE,
};
export default CAPTION_ADD_LOCALE;

View File

@ -0,0 +1,35 @@
import { useEffect } from 'react';
import { CaptionSaveCommandArguments } from 'bigbluebutton-html-plugin-sdk/dist/cjs/server-commands/caption/types';
import { useMutation } from '@apollo/client';
import * as uuidLib from 'uuid';
import {
CaptionCommandsEnum,
} from 'bigbluebutton-html-plugin-sdk/dist/cjs/server-commands/caption/enum';
import SUBMIT_TRANSCRIPT from './mutations';
const PluginSaveCaptionServerCommandsManager = () => {
const [submitTranscript] = useMutation(SUBMIT_TRANSCRIPT);
const handleSubmitCaption = ((
event: CustomEvent<CaptionSaveCommandArguments>,
) => {
submitTranscript({
variables: {
transcriptId: uuidLib.v4(),
transcript: event.detail.text,
locale: event.detail.locale,
captionType: event.detail.captionType,
},
});
}) as EventListener;
useEffect(() => {
window.addEventListener(CaptionCommandsEnum.SAVE, handleSubmitCaption);
return () => {
window.removeEventListener(CaptionCommandsEnum.SAVE, handleSubmitCaption);
};
}, []);
return null;
};
export default PluginSaveCaptionServerCommandsManager;

View File

@ -0,0 +1,19 @@
import { gql } from '@apollo/client';
const SUBMIT_TRANSCRIPT = gql`
mutation (
$transcriptId: String!
$transcript: String!
$locale: String!
$captionType: String!
) {
captionSubmitTranscript(
transcriptId: $transcriptId,
transcript: $transcript,
locale: $locale,
captionType: $captionType
)
}
`;
export default SUBMIT_TRANSCRIPT;

View File

@ -0,0 +1,12 @@
import * as React from 'react';
import PluginSaveCaptionServerCommandsManager from './caption/save/manager';
import PluginAddLocaleCaptionServerCommandsManager from './caption/add-locale/manager';
const PluginServerCommandsHandler = () => (
<>
<PluginSaveCaptionServerCommandsManager />
<PluginAddLocaleCaptionServerCommandsManager />
</>
);
export default PluginServerCommandsHandler;

View File

@ -1,59 +0,0 @@
import React, { useContext } from 'react';
import { GenericContentType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/generic-content-item/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.genericContentItems) {
const genericMainContent = pluginsExtensibleAreasAggregatedState.genericContentItems
.filter((g) => g.type === GenericContentType.SIDEKICK_AREA);
genericSidekickContentExtensibleArea = [...genericMainContent];
}
const genericSidekickContentId = (id) => PANELS.GENERIC_CONTENT_SIDEKICK + id;
if (genericSidekickContentExtensibleArea.length === 0) return null;
const genericSidekickContentNavButtons = genericSidekickContentExtensibleArea.map((gsc) => (
<Styled.Section>
<Styled.Container>
<Styled.SmallTitle>
{gsc.section}
</Styled.SmallTitle>
</Styled.Container>
<Styled.ScrollableList>
<Styled.List>
<Styled.ListItem
role="button"
tabIndex={0}
active={sidebarContentPanel === genericSidekickContentId(gsc.id)}
onClick={() => {
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),
});
}}
>
<Icon iconName={gsc.buttonIcon} />
<span>
{gsc.name}
</span>
</Styled.ListItem>
</Styled.List>
</Styled.ScrollableList>
</Styled.Section>
));
return <>{genericSidekickContentNavButtons}</>;
};
export default GenericSidekickContentNavButton;

View File

@ -0,0 +1,72 @@
import React, { useContext } from 'react';
import { GenericContentType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/generic-content-item/enums';
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
import Styled from './styles';
import { PANELS } from '/imports/ui/components/layout/enums';
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
import GenericContentSidekickAreaMenuItem from './menu-item/component';
interface GenericComponentSidekickMenuProps {
sidebarContentPanel: string;
layoutContextDispatch: (...args: unknown[]) => void;
}
interface MappedMenuItems {
[key: string]: PluginSdk.GenericContentSidekickArea[]
}
const GenericSidekickContentNavButton = ({
sidebarContentPanel, layoutContextDispatch,
}: GenericComponentSidekickMenuProps) => {
const { pluginsExtensibleAreasAggregatedState } = useContext(PluginsContext);
let genericSidekickContentExtensibleArea = [] as PluginSdk.GenericContentSidekickArea[];
if (pluginsExtensibleAreasAggregatedState.genericContentItems) {
const genericMainContent = pluginsExtensibleAreasAggregatedState.genericContentItems
.filter((g) => g.type === GenericContentType.SIDEKICK_AREA) as PluginSdk.GenericContentSidekickArea[];
genericSidekickContentExtensibleArea = [...genericMainContent];
}
const genericContentSidekickId = (id: string) => PANELS.GENERIC_CONTENT_SIDEKICK + id;
const groupBySidekickMenuSection: MappedMenuItems = {};
genericSidekickContentExtensibleArea.forEach((item) => {
const { section } = item;
let alreadySetArray = groupBySidekickMenuSection[section];
if (alreadySetArray) {
alreadySetArray.push(item);
} else {
alreadySetArray = [item];
}
groupBySidekickMenuSection[section] = alreadySetArray;
});
if (Object.keys(groupBySidekickMenuSection).length !== 0) {
return Object.keys(groupBySidekickMenuSection).map((section) => (
<Styled.Section
key={genericContentSidekickId(section)}
>
<Styled.Container>
<Styled.SmallTitle>
{section}
</Styled.SmallTitle>
</Styled.Container>
{groupBySidekickMenuSection[section].map((genericContentSidekickAreaObject) => (
<GenericContentSidekickAreaMenuItem
key={genericContentSidekickId(genericContentSidekickAreaObject.id)}
sidebarContentPanel={sidebarContentPanel}
genericSidekickContentId={genericContentSidekickId(genericContentSidekickAreaObject.id)}
genericContentSidekickAreaObject={genericContentSidekickAreaObject}
layoutContextDispatch={layoutContextDispatch}
/>
))}
</Styled.Section>
));
} else {
}
return null;
};
export default GenericSidekickContentNavButton;

View File

@ -0,0 +1,63 @@
import * as React from 'react';
import { useEffect } from 'react';
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
import Icon from '/imports/ui/components/common/icon/component';
import { ACTIONS, PANELS } from '/imports/ui/components/layout/enums';
import Styled from '../styles';
interface GenericContentSidekickAreaMenuItemProps{
sidebarContentPanel: string;
genericSidekickContentId: string;
genericContentSidekickAreaObject: PluginSdk.GenericContentSidekickArea;
layoutContextDispatch: (...args: unknown[]) => void;
}
const GenericContentSidekickAreaMenuItem = ({
sidebarContentPanel,
genericSidekickContentId,
genericContentSidekickAreaObject,
layoutContextDispatch,
}: GenericContentSidekickAreaMenuItemProps) => {
useEffect(() => {
if (genericContentSidekickAreaObject.open) {
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
value: true,
});
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
value: genericSidekickContentId,
});
}
}, []);
return (
<Styled.ScrollableList>
<Styled.List>
<Styled.ListItem
role="button"
tabIndex={0}
active={sidebarContentPanel === genericSidekickContentId}
onClick={() => {
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
value: sidebarContentPanel !== genericSidekickContentId,
});
layoutContextDispatch({
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
value: sidebarContentPanel === genericSidekickContentId
? PANELS.NONE
: genericSidekickContentId,
});
}}
>
<Icon iconName={genericContentSidekickAreaObject.buttonIcon} />
<span>
{genericContentSidekickAreaObject.name}
</span>
</Styled.ListItem>
</Styled.List>
</Styled.ScrollableList>
);
};
export default GenericContentSidekickAreaMenuItem;

View File

@ -3,7 +3,11 @@ 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)`
interface ListItemProps {
active: boolean;
}
const ListItem = styled(StyledContent.ListItem)<ListItemProps>`
i{ left: 4px; }
`;