Merge pull request #21278 from GuiLeme/lms-plugin-adaptation
This commit is contained in:
commit
eecd48ae30
@ -6,6 +6,7 @@ import Icon from "/imports/ui/components/common/icon/component";
|
||||
import { SMALL_VIEWPORT_BREAKPOINT } from '/imports/ui/components/layout/enums';
|
||||
import KEY_CODES from '/imports/utils/keyCodes';
|
||||
import MenuSkeleton from './skeleton';
|
||||
import GenericContentItem from '/imports/ui/components/generic-content/generic-content-item/component';
|
||||
import Styled from './styles';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
@ -102,7 +103,8 @@ class BBBMenu extends React.Component {
|
||||
|
||||
return actions?.map(a => {
|
||||
const { dataTest, label, onClick, key, disabled,
|
||||
description, selected, textColor, isToggle, loading } = a;
|
||||
description, selected, textColor, isToggle, loading,
|
||||
isTitle, titleActions, contentFunction } = a;
|
||||
const emojiSelected = key?.toLowerCase()?.includes(selectedEmoji?.toLowerCase());
|
||||
|
||||
let customStyles = {
|
||||
@ -149,7 +151,7 @@ class BBBMenu extends React.Component {
|
||||
isEmoji={isEmoji}
|
||||
>
|
||||
{a.icon ? <Icon iconName={a.icon} key="icon" /> : null}
|
||||
<Styled.Option isHorizontal={isHorizontal} isMobile={isMobile} aria-describedby={`${key}-option-desc`}>{label}</Styled.Option>
|
||||
<Styled.Option hasIcon={!!(a.icon)} isHorizontal={isHorizontal} isMobile={isMobile} aria-describedby={`${key}-option-desc`}>{label}</Styled.Option>
|
||||
{description && <div className="sr-only" id={`${key}-option-desc`}>{`${description}${selected ? ` - ${intl.formatMessage(intlMessages.active)}` : ''}`}</div>}
|
||||
{a.iconRight ? <Styled.IconRight iconName={a.iconRight} key="iconRight" /> : null}
|
||||
</Styled.MenuItemWrapper>
|
||||
@ -158,11 +160,38 @@ class BBBMenu extends React.Component {
|
||||
(!onClick && !a.isSeparator) && (
|
||||
<Styled.BBBMenuInformation
|
||||
key={a.key}
|
||||
isTitle={isTitle}
|
||||
isGenericContent={!!contentFunction}
|
||||
>
|
||||
<Styled.MenuItemWrapper>
|
||||
<Styled.MenuItemWrapper
|
||||
hasSpaceBetween={isTitle && titleActions}
|
||||
>
|
||||
{!contentFunction ? (
|
||||
<>
|
||||
{a.icon ? <Icon color={textColor} iconName={a.icon} key="icon" /> : null}
|
||||
<Styled.Option textColor={textColor} isHorizontal={isHorizontal} isMobile={isMobile} aria-describedby={`${key}-option-desc`}>{label}</Styled.Option>
|
||||
<Styled.Option hasIcon={!!(a.icon)} isTitle={isTitle} textColor={textColor} isHorizontal={isHorizontal} isMobile={isMobile} aria-describedby={`${key}-option-desc`}>{label}</Styled.Option>
|
||||
{a.iconRight ? <Styled.IconRight color={textColor} iconName={a.iconRight} key="iconRight" /> : null}
|
||||
{(isTitle && titleActions?.length > 0) ? (
|
||||
titleActions.map((item, index) => (
|
||||
<Styled.TitleAction
|
||||
key={item.id || index}
|
||||
tooltipplacement="right"
|
||||
size="md"
|
||||
onClick={item.onClick}
|
||||
circle
|
||||
tooltipLabel={item.tooltip}
|
||||
hideLabel
|
||||
icon={item.icon}
|
||||
/>
|
||||
))
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<GenericContentItem
|
||||
width="100%"
|
||||
renderFunction={contentFunction}
|
||||
/>
|
||||
)}
|
||||
</Styled.MenuItemWrapper>
|
||||
</Styled.BBBMenuInformation>
|
||||
),
|
||||
|
@ -2,8 +2,14 @@ import styled from 'styled-components';
|
||||
import Button from '/imports/ui/components/common/button/component';
|
||||
import Icon from '/imports/ui/components/common/icon/component';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import { colorWhite, colorPrimary } from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import { fontSizeLarge } from '/imports/ui/stylesheets/styled-components/typography';
|
||||
import {
|
||||
colorWhite,
|
||||
colorPrimary,
|
||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||
import {
|
||||
fontSizeLarge,
|
||||
headingsFontWeight,
|
||||
} from '/imports/ui/stylesheets/styled-components/typography';
|
||||
import { mediumUp } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
||||
import Menu from '@mui/material/Menu';
|
||||
|
||||
@ -36,16 +42,37 @@ const MenuItemWrapper = styled.div`
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
`}
|
||||
${({ hasSpaceBetween }) => hasSpaceBetween && `
|
||||
justify-content: space-between;
|
||||
`}
|
||||
`;
|
||||
|
||||
const TitleAction = styled(Button)`
|
||||
z-index: 3;
|
||||
margin-left: .1rem;
|
||||
& > span:first-child {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Option = styled.div`
|
||||
line-height: 1;
|
||||
margin-right: 1.65rem;
|
||||
${({ hasIcon }) => hasIcon && `
|
||||
margin-left: .5rem;
|
||||
`}
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
padding: .1rem 0;
|
||||
|
||||
${({ isTitle }) => isTitle && `
|
||||
margin-left: .1rem;
|
||||
padding: .1rem 0 0 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: ${headingsFontWeight};
|
||||
`}
|
||||
|
||||
[dir="rtl"] & {
|
||||
margin-right: .5rem;
|
||||
margin-left: 1.65rem;
|
||||
@ -90,7 +117,15 @@ const IconRight = styled(Icon)`
|
||||
`;
|
||||
|
||||
const BBBMenuInformation = styled.div`
|
||||
${({ isGenericContent }) => ((isGenericContent) ? `
|
||||
padding: 0 16px;
|
||||
` : `
|
||||
padding: 12px 16px;
|
||||
`)}
|
||||
${({ isTitle }) => (isTitle) && `
|
||||
min-width: 15rem;
|
||||
padding: 12px 16px 8px 16px;
|
||||
`}
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
@ -164,6 +199,7 @@ const SkeletonWrapper = styled.span`
|
||||
`;
|
||||
|
||||
export default {
|
||||
TitleAction,
|
||||
MenuWrapper,
|
||||
MenuItemWrapper,
|
||||
Option,
|
||||
|
@ -4,6 +4,7 @@ import { GenericContentItemProps } from './types';
|
||||
const GenericContentItem: React.FC<GenericContentItemProps> = (props) => {
|
||||
const {
|
||||
renderFunction,
|
||||
width,
|
||||
} = props;
|
||||
const elementRef = useRef(null);
|
||||
|
||||
@ -13,12 +14,16 @@ const GenericContentItem: React.FC<GenericContentItemProps> = (props) => {
|
||||
}
|
||||
}, [elementRef]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
const style: React.CSSProperties = {
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
};
|
||||
if (width) {
|
||||
style.width = width;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
ref={elementRef}
|
||||
/>
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
export interface GenericContentItemProps {
|
||||
renderFunction: (element: HTMLElement) => void;
|
||||
width?: string;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
||||
import useDeduplicatedSubscription from '/imports/ui/core/hooks/useDeduplicatedSubscription';
|
||||
import { MEETING_PERMISSIONS_SUBSCRIPTION } from '../queries';
|
||||
import { setLocalUserList, useLoadedUserList } from '/imports/ui/core/hooks/useLoadedUserList';
|
||||
@ -13,6 +14,7 @@ import ListItem from '../list-item/component';
|
||||
import { layoutSelect } from '/imports/ui/components/layout/context';
|
||||
import { Layout } from '/imports/ui/components/layout/layoutTypes';
|
||||
import SkeletonUserListItem from '../list-item/skeleton/component';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
|
||||
interface UserListParticipantsContainerProps {
|
||||
index: number;
|
||||
@ -36,6 +38,14 @@ const UsersListParticipantsPage: React.FC<UsersListParticipantsPage> = ({
|
||||
}) => {
|
||||
const [openUserAction, setOpenUserAction] = React.useState<string | null>(null);
|
||||
const isRTL = layoutSelect((i: Layout) => i.isRTL);
|
||||
const { pluginsExtensibleAreasAggregatedState } = useContext(PluginsContext);
|
||||
let userListDropdownItems = [] as PluginSdk.UserListDropdownInterface[];
|
||||
if (pluginsExtensibleAreasAggregatedState.userListDropdownItems) {
|
||||
userListDropdownItems = [
|
||||
...pluginsExtensibleAreasAggregatedState.userListDropdownItems,
|
||||
];
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
@ -49,6 +59,7 @@ const UsersListParticipantsPage: React.FC<UsersListParticipantsPage> = ({
|
||||
usersPolicies={meeting.usersPolicies}
|
||||
isBreakout={meeting.isBreakout}
|
||||
pageId={pageId}
|
||||
userListDropdownItems={userListDropdownItems}
|
||||
open={user.userId === openUserAction}
|
||||
setOpenUserAction={setOpenUserAction}
|
||||
>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { User } from '/imports/ui/Types/user';
|
||||
import { LockSettings, UsersPolicies } from '/imports/ui/Types/meeting';
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
@ -34,7 +34,6 @@ import ConfirmationModal from '/imports/ui/components/common/modal/confirmation/
|
||||
|
||||
import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||
import { setPendingChat } from '/imports/ui/core/local-states/usePendingChat';
|
||||
import { PluginsContext } from '/imports/ui/components/components-data/plugin-context/context';
|
||||
import Styled from './styles';
|
||||
import { useMutation, useLazyQuery } from '@apollo/client';
|
||||
import { CURRENT_PAGE_WRITERS_QUERY } from '/imports/ui/components/whiteboard/queries';
|
||||
@ -43,6 +42,7 @@ import useToggleVoice from '/imports/ui/components/audio/audio-graphql/hooks/use
|
||||
import useWhoIsUnmuted from '/imports/ui/core/hooks/useWhoIsUnmuted';
|
||||
|
||||
interface UserActionsProps {
|
||||
userListDropdownItems: PluginSdk.UserListDropdownInterface[];
|
||||
user: User;
|
||||
currentUser: User;
|
||||
lockSettings: LockSettings;
|
||||
@ -56,14 +56,15 @@ interface UserActionsProps {
|
||||
|
||||
interface DropdownItem {
|
||||
key: string;
|
||||
label: string | undefined;
|
||||
icon: string | undefined;
|
||||
tooltip: string | undefined;
|
||||
allowed: boolean | undefined;
|
||||
iconRight: string | undefined;
|
||||
textColor: string | undefined;
|
||||
isSeparator: boolean | undefined;
|
||||
onClick: (() => void) | undefined;
|
||||
label?: string;
|
||||
icon?: string;
|
||||
tooltip?: string;
|
||||
allowed?: boolean;
|
||||
iconRight?: string;
|
||||
textColor?: string;
|
||||
isSeparator?: boolean;
|
||||
contentFunction?: ((element: HTMLElement) => void);
|
||||
onClick?: (() => void);
|
||||
}
|
||||
|
||||
interface Writer {
|
||||
@ -171,8 +172,8 @@ const makeDropdownPluginItem: (
|
||||
returnValue.onClick = dropdownButton.onClick;
|
||||
break;
|
||||
}
|
||||
case UserListDropdownItemType.INFORMATION: {
|
||||
const dropdownButton = userDropdownItem as PluginSdk.UserListDropdownInformation;
|
||||
case UserListDropdownItemType.FIXED_CONTENT_INFORMATION: {
|
||||
const dropdownButton = userDropdownItem as PluginSdk.UserListDropdownFixedContentInformation;
|
||||
returnValue.label = dropdownButton.label;
|
||||
returnValue.icon = dropdownButton.icon;
|
||||
returnValue.iconRight = dropdownButton.iconRight;
|
||||
@ -180,6 +181,12 @@ const makeDropdownPluginItem: (
|
||||
returnValue.allowed = dropdownButton.allowed;
|
||||
break;
|
||||
}
|
||||
case UserListDropdownItemType.GENERIC_CONTENT_INFORMATION: {
|
||||
const dropdownButton = userDropdownItem as PluginSdk.UserListDropdownGenericContentInformation;
|
||||
returnValue.allowed = dropdownButton.allowed;
|
||||
returnValue.contentFunction = dropdownButton.contentFunction;
|
||||
break;
|
||||
}
|
||||
case UserListDropdownItemType.SEPARATOR: {
|
||||
returnValue.allowed = true;
|
||||
returnValue.isSeparator = true;
|
||||
@ -200,6 +207,7 @@ const UserActions: React.FC<UserActionsProps> = ({
|
||||
isBreakout,
|
||||
children,
|
||||
pageId,
|
||||
userListDropdownItems,
|
||||
open,
|
||||
setOpenUserAction,
|
||||
}) => {
|
||||
@ -251,8 +259,6 @@ const UserActions: React.FC<UserActionsProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const { pluginsExtensibleAreasAggregatedState } = useContext(PluginsContext);
|
||||
|
||||
const { data: unmutedUsers } = useWhoIsUnmuted();
|
||||
const isMuted = !unmutedUsers[user.userId];
|
||||
|
||||
@ -283,13 +289,6 @@ const UserActions: React.FC<UserActionsProps> = ({
|
||||
|
||||
const userChatLocked = user.userLockSettings?.disablePublicChat;
|
||||
|
||||
let userListDropdownItems = [] as PluginSdk.UserListDropdownInterface[];
|
||||
if (pluginsExtensibleAreasAggregatedState.userListDropdownItems) {
|
||||
userListDropdownItems = [
|
||||
...pluginsExtensibleAreasAggregatedState.userListDropdownItems,
|
||||
];
|
||||
}
|
||||
|
||||
const userDropdownItems = userListDropdownItems.filter(
|
||||
(item: PluginSdk.UserListDropdownInterface) => (user?.userId === item?.userId),
|
||||
);
|
||||
@ -325,10 +324,25 @@ const UserActions: React.FC<UserActionsProps> = ({
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const titleActions = userDropdownItems.filter(
|
||||
(item: PluginSdk.UserListDropdownInterface) => (
|
||||
item?.type === UserListDropdownItemType.TITLE_ACTION),
|
||||
);
|
||||
const dropdownOptions = [
|
||||
{
|
||||
allowed: true,
|
||||
key: 'userName',
|
||||
label: user.name,
|
||||
titleActions,
|
||||
isTitle: true,
|
||||
},
|
||||
...makeDropdownPluginItem(userDropdownItems.filter(
|
||||
(item: PluginSdk.UserListDropdownInterface) => (item?.type === UserListDropdownItemType.INFORMATION),
|
||||
(item: PluginSdk.UserListDropdownInterface) => (
|
||||
item?.type === UserListDropdownItemType.FIXED_CONTENT_INFORMATION
|
||||
|| item?.type === UserListDropdownItemType.GENERIC_CONTENT_INFORMATION
|
||||
|| (item?.type === UserListDropdownItemType.SEPARATOR
|
||||
&& (item as PluginSdk.UserListDropdownSeparator)?.position
|
||||
=== PluginSdk.UserListDropdownSeparatorPosition.BEFORE)),
|
||||
)),
|
||||
{
|
||||
allowed: user.cameras.length > 0
|
||||
@ -540,7 +554,13 @@ const UserActions: React.FC<UserActionsProps> = ({
|
||||
dataTest: 'ejectCamera',
|
||||
},
|
||||
...makeDropdownPluginItem(userDropdownItems.filter(
|
||||
(item: PluginSdk.UserListDropdownInterface) => (item?.type !== UserListDropdownItemType.INFORMATION),
|
||||
(item: PluginSdk.UserListDropdownInterface) => (
|
||||
item?.type !== UserListDropdownItemType.FIXED_CONTENT_INFORMATION
|
||||
&& item?.type !== UserListDropdownItemType.GENERIC_CONTENT_INFORMATION
|
||||
&& !(item?.type === UserListDropdownItemType.SEPARATOR
|
||||
&& (item as PluginSdk.UserListDropdownSeparator)?.position
|
||||
=== PluginSdk.UserListDropdownSeparatorPosition.BEFORE)
|
||||
),
|
||||
)),
|
||||
];
|
||||
|
||||
|
8
bigbluebutton-html5/package-lock.json
generated
8
bigbluebutton-html5/package-lock.json
generated
@ -27,7 +27,7 @@
|
||||
"autoprefixer": "^10.4.4",
|
||||
"axios": "^1.7.4",
|
||||
"babel-runtime": "~6.26.0",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.58",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.59",
|
||||
"bowser": "^2.11.0",
|
||||
"browser-bunyan": "^1.8.0",
|
||||
"classnames": "^2.2.6",
|
||||
@ -5975,9 +5975,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bigbluebutton-html-plugin-sdk": {
|
||||
"version": "0.0.58",
|
||||
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.58.tgz",
|
||||
"integrity": "sha512-x4Dnt2TiWlosM8vOqq/FCsuG8XCZ//Ab79b/5w7S8nRgeXWmIc942R4tbWZ7CwBGAdGnZvp3mAAqkvB7SYfk0g==",
|
||||
"version": "0.0.59",
|
||||
"resolved": "https://registry.npmjs.org/bigbluebutton-html-plugin-sdk/-/bigbluebutton-html-plugin-sdk-0.0.59.tgz",
|
||||
"integrity": "sha512-HYmV9vkbC8M3CcKizCJMzgYaEA9w3fbRbuGtqDhHFQ0hFrsGP/Cd86gT+toFXKNoG1KatYwhZdMXk4eXNMjywg==",
|
||||
"license": "LGPL-3.0",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.8.7",
|
||||
|
@ -56,7 +56,7 @@
|
||||
"autoprefixer": "^10.4.4",
|
||||
"axios": "^1.7.4",
|
||||
"babel-runtime": "~6.26.0",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.58",
|
||||
"bigbluebutton-html-plugin-sdk": "0.0.59",
|
||||
"bowser": "^2.11.0",
|
||||
"browser-bunyan": "^1.8.0",
|
||||
"classnames": "^2.2.6",
|
||||
|
Loading…
Reference in New Issue
Block a user