{
const isModerator = currentUser.role === ROLE_MODERATOR;
const { maxStickyNoteLength, maxNumberOfAnnotations } = WHITEBOARD_CONFIG;
const fontFamily = WHITEBOARD_CONFIG.styles.text.family;
- const fullscreen = layoutSelect((i) => i.fullscreen);
const handleToggleFullScreen = (ref) =>
FullscreenService.toggleFullScreen(ref);
const layoutContextDispatch = layoutDispatch();
@@ -84,7 +83,6 @@ const WhiteboardContainer = (props) => {
maxStickyNoteLength,
maxNumberOfAnnotations,
fontFamily,
- fullscreen,
hasShapeAccess,
handleToggleFullScreen,
sidebarNavigationWidth,
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/component.jsx
deleted file mode 100644
index afbe440b1090..000000000000
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/component.jsx
+++ /dev/null
@@ -1,317 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import { toPng } from 'html-to-image';
-import { toast } from 'react-toastify';
-import logger from '/imports/startup/client/logger';
-import Styled from '/imports/ui/components/presentation/presentation-menu/styles';
-import BBBMenu from './menu/component';
-import TooltipContainer from '/imports/ui/components/common/tooltip/container';
-import { ACTIONS } from '/imports/ui/components/layout/enums';
-import browserInfo from '/imports/utils/browserInfo';
-import AppService from '/imports/ui/components/app/service';
-
-const propTypes = {
- handleToggleFullscreen: PropTypes.func.isRequired,
- isFullscreen: PropTypes.bool,
- elementName: PropTypes.string,
- fullscreenRef: PropTypes.instanceOf(Element),
- meetingName: PropTypes.string,
- isIphone: PropTypes.bool,
- elementId: PropTypes.string,
- elementGroup: PropTypes.string,
- currentElement: PropTypes.string,
- currentGroup: PropTypes.string,
- layoutContextDispatch: PropTypes.func.isRequired,
- isRTL: PropTypes.bool,
- tldrawAPI: PropTypes.shape({
- copySvg: PropTypes.func.isRequired,
- getShapes: PropTypes.func.isRequired,
- currentPageId: PropTypes.string.isRequired,
- }),
-};
-
-const defaultProps = {
- isIphone: false,
- isFullscreen: false,
- isRTL: false,
- elementName: '',
- meetingName: '',
- fullscreenRef: null,
- elementId: '',
- elementGroup: '',
- currentElement: '',
- currentGroup: '',
- tldrawAPI: null,
-};
-
-const PresentationOps = (props) => {
- const {
- isFullscreen,
- elementId,
- elementName,
- elementGroup,
- currentElement,
- currentGroup,
- fullscreenRef,
- tldrawAPI,
- handleToggleFullscreen,
- layoutContextDispatch,
- meetingName,
- isIphone,
- isRTL,
- isToolbarVisible,
- setIsToolbarVisible,
- exitFullscreenLabel,
- fullscreenLabel,
- hideToolsDesc,
- showToolsDesc,
- downloading,
- downloaded,
- downloadFailed,
- snapshotLabel,
- hasWBAccess,
- amIPresenter,
- optionsLabel,
- whiteboardLabel,
- } = props;
-
- const [state, setState] = useState({
- hasError: false,
- loading: false,
- });
-
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const toastId = useRef(null);
- const dropdownRef = useRef(null);
-
- const formattedLabel = (fullscreen) => (fullscreen
- ? exitFullscreenLabel
- : fullscreenLabel
- );
-
- const formattedVisibilityLabel = (visible) => (visible
- ? hideToolsDesc
- : showToolsDesc
- );
-
- function renderToastContent() {
- const { loading, hasError } = state;
-
- let icon = loading ? 'blank' : 'check';
- if (hasError) icon = 'circle_close';
-
- return (
-
-
-
- {loading && !hasError && downloading}
- {!loading && !hasError && downloaded}
- {!loading && hasError && downloadFailed}
-
-
-
-
-
-
- );
- }
-
- function getAvailableOptions() {
- const menuItems = [];
-
- if (!isIphone) {
- menuItems.push(
- {
- key: 'list-item-fullscreen',
- dataTest: 'presentationFullscreen',
- label: formattedLabel(isFullscreen),
- icon: isFullscreen ? 'exit_fullscreen' : 'fullscreen',
- onClick: () => {
- handleToggleFullscreen(fullscreenRef);
- const newElement = (elementId === currentElement) ? '' : elementId;
- const newGroup = (elementGroup === currentGroup) ? '' : elementGroup;
-
- layoutContextDispatch({
- type: ACTIONS.SET_FULLSCREEN_ELEMENT,
- value: {
- element: newElement,
- group: newGroup,
- },
- });
- },
- },
- );
- }
-
- const { isSafari } = browserInfo;
-
- if (!isSafari) {
- menuItems.push(
- {
- key: 'list-item-screenshot',
- label: snapshotLabel,
- dataTest: 'presentationSnapshot',
- icon: 'video',
- onClick: async () => {
- setState({
- loading: true,
- hasError: false,
- });
-
- toastId.current = toast.info(renderToastContent(), {
- hideProgressBar: true,
- autoClose: false,
- newestOnTop: true,
- closeOnClick: true,
- onClose: () => {
- toastId.current = null;
- },
- });
-
- // This is a workaround to a conflict of the
- // dark mode's styles and the html-to-image lib.
- // Issue:
- // https://github.com/bubkoo/html-to-image/issues/370
- const darkThemeState = AppService.isDarkThemeEnabled();
- AppService.setDarkTheme(false);
-
- try {
- const { copySvg, getShapes, currentPageId } = tldrawAPI;
- const svgString = await copySvg(getShapes(currentPageId).map((shape) => shape.id));
- const container = document.createElement('div');
- container.innerHTML = svgString;
- const svgElem = container.firstChild;
- const width = svgElem?.width?.baseVal?.value ?? window.screen.width;
- const height = svgElem?.height?.baseVal?.value ?? window.screen.height;
-
- const data = await toPng(svgElem, { width, height, backgroundColor: '#FFF' });
-
- const anchor = document.createElement('a');
- anchor.href = data;
- anchor.setAttribute(
- 'download',
- `${elementName}_${meetingName}_${new Date().toISOString()}.png`,
- );
- anchor.click();
-
- setState({
- loading: false,
- hasError: false,
- });
- } catch (e) {
- setState({
- loading: false,
- hasError: true,
- });
-
- logger.warn({
- logCode: 'presentation_snapshot_error',
- extraInfo: e,
- });
- } finally {
- // Workaround
- AppService.setDarkTheme(darkThemeState);
- }
- },
- },
- );
- }
-
- const tools = document.querySelector('#TD-Tools');
- if (tools && (hasWBAccess || amIPresenter)){
- menuItems.push(
- {
- key: 'list-item-toolvisibility',
- dataTest: 'toolVisibility',
- label: formattedVisibilityLabel(isToolbarVisible),
- icon: isToolbarVisible ? 'close' : 'pen_tool',
- onClick: () => {
- setIsToolbarVisible(!isToolbarVisible);
- },
- },
- );
- }
-
- return menuItems;
- }
-
- useEffect(() => {
- if (toastId.current) {
- toast.update(toastId.current, {
- render: renderToastContent(),
- hideProgressBar: state.loading,
- autoClose: state.loading ? false : 3000,
- newestOnTop: true,
- closeOnClick: true,
- onClose: () => {
- toastId.current = null;
- },
- });
- }
-
- if (dropdownRef.current) {
- document.activeElement.blur();
- dropdownRef.current.focus();
- }
- });
-
- const options = getAvailableOptions();
-
- if (options.length === 0) {
- const undoCtrls = document.getElementById('TD-Styles')?.nextSibling;
- if (undoCtrls?.style) {
- undoCtrls.style = 'padding:0px';
- }
- const styleTool = document.getElementById('TD-Styles')?.parentNode;
- if (styleTool?.style) {
- styleTool.style = 'right:0px';
- }
- return null;
- }
-
- return (
-
-
- {
- setIsDropdownOpen((isOpen) => !isOpen);
- }}
- >
-
-
-
- )}
- opts={{
- id: 'presentation-dropdown-menu',
- keepMounted: true,
- transitionDuration: 0,
- elevation: 3,
- getContentAnchorEl: null,
- fullwidth: 'true',
- anchorOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
- transformOrigin: { vertical: 'top', horizontal: isRTL ? 'right' : 'left' },
- container: fullscreenRef,
- }}
- actions={options}
- />
-
- );
-};
-
-PresentationOps.propTypes = propTypes;
-PresentationOps.defaultProps = defaultProps;
-
-export default PresentationOps;
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/container.jsx
deleted file mode 100644
index a54adfe070a8..000000000000
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/container.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { withTracker } from 'meteor/react-meteor-data';
-import PresentationOps from './component';
-import FullscreenService from '/imports/ui/components/common/fullscreen-button/service';
-import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/api/meetings';
-import WhiteboardService from '/imports/ui/components/whiteboard/service';
-import UserService from '/imports/ui/components/user-list/service';
-
-const PresentationOpsContainer = (props) => {
- const { elementId, isRTL, layoutContextDispatch, fullscreen } = props;
- const { element: currentElement, group: currentGroup } = fullscreen;
- const isFullscreen = currentElement === elementId;
-
- return (
-
- );
-};
-
-export default withTracker((props) => {
- const handleToggleFullscreen = (ref) => FullscreenService.toggleFullScreen(ref);
- const isIphone = !!(navigator.userAgent.match(/iPhone/i));
- const meetingId = Auth.meetingID;
- const meetingObject = Meetings.findOne({ meetingId }, { fields: { 'meetingProp.name': 1 } });
- const hasWBAccess = WhiteboardService.hasMultiUserAccess(
- WhiteboardService.getCurrentWhiteboardId(),
- Auth.userID
- );
- const amIPresenter = UserService.isUserPresenter(Auth.userID);
- return {
- ...props,
- handleToggleFullscreen,
- isIphone,
- isDropdownOpen: Session.get('dropdownOpen'),
- meetingName: meetingObject.meetingProp.name,
- hasWBAccess,
- amIPresenter,
- };
-})(PresentationOpsContainer);
-
-PresentationOpsContainer.propTypes = {
- elementId: PropTypes.string.isRequired,
-};
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/menu/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/menu/component.jsx
deleted file mode 100644
index 5ec052c61217..000000000000
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/presentation-ops-injector/menu/component.jsx
+++ /dev/null
@@ -1,253 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-
-import Menu from "@material-ui/core/Menu";
-import { Divider } from "@material-ui/core";
-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 { ENTER } from "/imports/utils/keyCodes";
-
-import Styled from '/imports/ui/components/common/menu/styles';
-
-class BBBMenu extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- anchorEl: null,
- };
-
- this.optsToMerge = {};
- this.autoFocus = false;
-
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handleClick = this.handleClick.bind(this);
- this.handleClose = this.handleClose.bind(this);
- }
-
- componentDidUpdate() {
- const { anchorEl } = this.state;
- const { open } = this.props;
- if (open === false && anchorEl) {
- this.setState({ anchorEl: null });
- } else if (open === true && !anchorEl) {
- this.setState({ anchorEl: this.anchorElRef });
- }
- }
-
- handleKeyDown(event) {
- const { anchorEl } = this.state;
- const isMenuOpen = Boolean(anchorEl);
-
-
- if ([KEY_CODES.ESCAPE, KEY_CODES.TAB].includes(event.which)) {
- this.handleClose();
- return;
- }
-
- if (isMenuOpen && [KEY_CODES.ARROW_UP, KEY_CODES.ARROW_DOWN].includes(event.which)) {
- event.preventDefault();
- event.stopPropagation();
- const menuItems = Array.from(document.querySelectorAll('[data-key^="menuItem-"]'));
- if (menuItems.length === 0) return;
-
- const focusedIndex = menuItems.findIndex(item => item === document.activeElement);
- const nextIndex = event.which === KEY_CODES.ARROW_UP ? focusedIndex - 1 : focusedIndex + 1;
- let indexToFocus = 0;
- if (nextIndex < 0) {
- indexToFocus = menuItems.length - 1;
- } else if (nextIndex >= menuItems.length) {
- indexToFocus = 0;
- } else {
- indexToFocus = nextIndex;
- }
-
- menuItems[indexToFocus].focus();
- }
- };
-
- handleClick(event) {
- this.setState({ anchorEl: event.currentTarget });
- };
-
- handleClose(event) {
- const { onCloseCallback } = this.props;
- this.setState({ anchorEl: null }, onCloseCallback());
-
- if (event) {
- event.persist();
-
- if (event.type === 'click') {
- setTimeout(() => {
- document.activeElement.blur();
- }, 0);
- }
- }
- };
-
- makeMenuItems() {
- const { actions, selectedEmoji, activeLabel } = this.props;
-
- return actions?.map(a => {
- const { dataTest, label, onClick, key, disabled, description, selected } = a;
- const emojiSelected = key?.toLowerCase()?.includes(selectedEmoji?.toLowerCase());
-
- let customStyles = {
- paddingLeft: '16px',
- paddingRight: '16px',
- paddingTop: '12px',
- paddingBottom: '12px',
- marginLeft: '0px',
- marginRight: '0px',
- };
-
- if (a.customStyles) {
- customStyles = { ...customStyles, ...a.customStyles };
- }
-
- return [
- a.dividerTop && ,
- {
- onClick();
- const close = !key?.includes('setstatus') && !key?.includes('back');
- // prevent menu close for sub menu actions
- if (close) this.handleClose(event);
- event.stopPropagation();
- }}>
-
- {a.icon ? : null}
- {label}
- {description && {`${description}${selected ? ` - ${activeLabel}` : ''}`}
}
- {a.iconRight ? : null}
-
- ,
- a.divider &&
- ];
- });
- }
-
- render() {
- const { anchorEl } = this.state;
- const { trigger, customStyles, dataTest, opts, accessKey, closeLabel } = this.props;
- const actionsItems = this.makeMenuItems();
-
- let menuStyles = { zIndex: 9999 };
-
- if (customStyles) {
- menuStyles = { ...menuStyles, ...customStyles };
- }
-
- return (
- <>
- {
- e.persist();
- // 1 = mouse, 5 = touch (firefox only)
- const firefoxInputSource = !([1, 5].includes(e.nativeEvent.mozInputSource));
- const chromeInputSource = !(['mouse', 'touch'].includes(e.nativeEvent.pointerType));
- this.optsToMerge.autoFocus = firefoxInputSource && chromeInputSource;
- this.handleClick(e);
- }}
- onKeyPress={(e) => {
- e.persist();
- if (e.which !== ENTER) return null;
- return this.handleClick(e);
- }}
- accessKey={accessKey}
- ref={(ref) => { this.anchorElRef = ref; return null; }}
- role="button"
- tabIndex={-1}
- >
- {trigger}
-
-
-
- >
- );
- }
-}
-
-export default BBBMenu;
-
-BBBMenu.defaultProps = {
- opts: {
- id: "default-dropdown-menu",
- autoFocus: false,
- keepMounted: true,
- transitionDuration: 0,
- elevation: 3,
- getContentAnchorEl: null,
- fullwidth: "true",
- anchorOrigin: { vertical: 'top', horizontal: 'right' },
- transformorigin: { vertical: 'top', horizontal: 'right' },
- },
- dataTest: "",
- onCloseCallback: () => { },
-};
-
-BBBMenu.propTypes = {
- trigger: PropTypes.element.isRequired,
-
- actions: PropTypes.arrayOf(PropTypes.shape({
- key: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- onClick: PropTypes.func,
- icon: PropTypes.string,
- iconRight: PropTypes.string,
- disabled: PropTypes.bool,
- divider: PropTypes.bool,
- dividerTop: PropTypes.bool,
- accessKey: PropTypes.string,
- dataTest: PropTypes.string,
- })).isRequired,
-
- opts: PropTypes.shape({
- id: PropTypes.string,
- autoFocus: PropTypes.bool,
- keepMounted: PropTypes.bool,
- transitionDuration: PropTypes.number,
- elevation: PropTypes.number,
- getContentAnchorEl: PropTypes.element,
- fullwidth: PropTypes.string,
- anchorOrigin: PropTypes.shape({
- vertical: PropTypes.string,
- horizontal: PropTypes.string
- }),
- transformorigin: PropTypes.shape({
- vertical: PropTypes.string,
- horizontal: PropTypes.string
- }),
- }),
-
- onCloseCallback: PropTypes.func,
- dataTest: PropTypes.string,
-};
\ No newline at end of file