Merge pull request #21278 from GuiLeme/lms-plugin-adaptation

This commit is contained in:
Tiago Jacobs 2024-10-02 21:12:33 -03:00 committed by GitHub
commit eecd48ae30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 146 additions and 44 deletions

View File

@ -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>
{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>
{a.iconRight ? <Styled.IconRight color={textColor} iconName={a.iconRight} key="iconRight" /> : null}
<Styled.MenuItemWrapper
hasSpaceBetween={isTitle && titleActions}
>
{!contentFunction ? (
<>
{a.icon ? <Icon color={textColor} iconName={a.icon} key="icon" /> : null}
<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>
),

View File

@ -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;
margin-left: .5rem;
${({ 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`
padding: 12px 16px;
${({ 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,

View File

@ -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]);
const style: React.CSSProperties = {
height: '100%',
overflow: 'hidden',
};
if (width) {
style.width = width;
}
return (
<div
style={{
height: '100%',
overflow: 'hidden',
}}
style={style}
ref={elementRef}
/>
);

View File

@ -1,3 +1,4 @@
export interface GenericContentItemProps {
renderFunction: (element: HTMLElement) => void;
width?: string;
}

View File

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

View File

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

View File

@ -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",

View File

@ -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",