diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx index d87edbf71a..4ea3354d0c 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx @@ -249,6 +249,7 @@ class ActionsDropdown extends PureComponent { presentations, setPresentation, podIds, + setPresentationFitToWidth, } = this.props; if (!podIds || podIds.length < 1) return []; @@ -272,6 +273,7 @@ class ActionsDropdown extends PureComponent { description: "uploaded presentation file", key: `uploaded-presentation-${p.id}`, onClick: () => { + setPresentationFitToWidth(false); setPresentation(p.id, podId); }, } diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx index d0e1d262ff..d1b9b216e1 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx @@ -39,6 +39,7 @@ class ActionsBar extends PureComponent { setMeetingLayout, showPushLayout, setPushLayout, + setPresentationFitToWidth, } = this.props; const shouldShowOptionsButton = (isPresentationEnabled() && isThereCurrentPresentation) @@ -67,6 +68,7 @@ class ActionsBar extends PureComponent { setPushLayout, presentationIsOpen, showPushLayout, + setPresentationFitToWidth, }} /> {isCaptionsAvailable diff --git a/bigbluebutton-html5/imports/ui/components/app/component.jsx b/bigbluebutton-html5/imports/ui/components/app/component.jsx index 5a62095241..572465eb82 100644 --- a/bigbluebutton-html5/imports/ui/components/app/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/component.jsx @@ -131,8 +131,10 @@ class App extends Component { super(props); this.state = { enableResize: !window.matchMedia(MOBILE_MEDIA).matches, + presentationFitToWidth: false, }; + this.setPresentationFitToWidth = this.setPresentationFitToWidth.bind(this); this.handleWindowResize = throttle(this.handleWindowResize).bind(this); this.shouldAriaHide = this.shouldAriaHide.bind(this); @@ -283,6 +285,10 @@ class App extends Component { ConnectionStatusService.stopRoundTripTime(); } + setPresentationFitToWidth(presentationFitToWidth) { + this.setState({ presentationFitToWidth }); + } + handleWindowResize() { const { enableResize } = this.state; const shouldEnableResize = !window.matchMedia(MOBILE_MEDIA).matches; @@ -403,6 +409,7 @@ class App extends Component { setMeetingLayout={setMeetingLayout} showPushLayout={showPushLayoutButton && selectedLayout === 'custom'} presentationIsOpen={presentationIsOpen} + setPresentationFitToWidth={this.setPresentationFitToWidth} /> ); @@ -517,6 +524,8 @@ class App extends Component { darkTheme, } = this.props; + const { presentationFitToWidth } = this.state; + return ( <> @@ -540,7 +549,7 @@ class App extends Component { - {shouldShowPresentation ? : null} + {shouldShowPresentation ? : null} {shouldShowScreenshare ? : null} { shouldShowExternalVideo diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx index bf46dca8d1..1e58599077 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx @@ -74,7 +74,6 @@ class Presentation extends PureComponent { presentationWidth: 0, presentationHeight: 0, zoom: 100, - fitToWidth: false, isFullscreen: false, tldrawAPI: null, isPanning: false, @@ -86,7 +85,6 @@ class Presentation extends PureComponent { this.currentPresentationToastId = null; this.getSvgRef = this.getSvgRef.bind(this); - this.setFitToWidth = this.setFitToWidth.bind(this); this.zoomChanger = debounce({ delay: 200 }, this.zoomChanger.bind(this)); this.updateLocalPosition = this.updateLocalPosition.bind(this); this.panAndZoomChanger = this.panAndZoomChanger.bind(this); @@ -207,13 +205,13 @@ class Presentation extends PureComponent { multiUser, numPages, currentPresentationId, + fitToWidth, } = this.props; const { presentationWidth, presentationHeight, zoom, isPanning, - fitToWidth, presentationId, hadPresentation, } = this.state; @@ -505,19 +503,14 @@ class Presentation extends PureComponent { } } - setFitToWidth(fitToWidth) { - this.setState({ fitToWidth }); - } - zoomChanger(zoom) { this.setState({ zoom }); } fitToWidthHandler() { - const { fitToWidth } = this.state; - + const { setPresentationFitToWidth, fitToWidth } = this.props; + setPresentationFitToWidth(!fitToWidth) this.setState({ - fitToWidth: !fitToWidth, zoom: HUNDRED_PERCENT, }); } @@ -535,9 +528,9 @@ class Presentation extends PureComponent { } calculateSize(viewBoxDimensions) { - const { presentationHeight, presentationWidth, fitToWidth } = this.state; + const { presentationHeight, presentationWidth } = this.state; - const { userIsPresenter, currentSlide, slidePosition } = this.props; + const { userIsPresenter, currentSlide, slidePosition, fitToWidth } = this.props; if (!currentSlide || !slidePosition) { return { width: 0, height: 0 }; @@ -605,8 +598,9 @@ class Presentation extends PureComponent { removeWhiteboardGlobalAccess, multiUserSize, multiUser, + fitToWidth, } = this.props; - const { zoom, fitToWidth, isPanning } = this.state; + const { zoom, isPanning } = this.state; if (!currentSlide) return null; @@ -733,12 +727,12 @@ class Presentation extends PureComponent { layoutContextDispatch, presentationIsOpen, darkTheme, + fitToWidth, } = this.props; const { isFullscreen, localPosition, - fitToWidth, zoom, tldrawIsMounting, isPanning, diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/component.jsx index cd351d329f..43e4e0a515 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/component.jsx @@ -7,13 +7,15 @@ const PresentationArea = ({ height, presentationIsOpen, darkTheme, + setPresentationFitToWidth, + fitToWidth, }) => { const presentationAreaSize = { presentationAreaWidth: width, presentationAreaHeight: height, }; return ( - + ); }; diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/container.jsx index fc76dfad02..8b8a66004d 100644 --- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-area/container.jsx @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import { layoutSelectOutput } from '../../layout/context'; import PresentationArea from './component'; -const PresentationAreaContainer = ({ presentationIsOpen, darkTheme }) => { +const PresentationAreaContainer = ({ presentationIsOpen, darkTheme, setPresentationFitToWidth, fitToWidth }) => { const presentation = layoutSelectOutput((i) => i.presentation); - return ; + return ; }; export default PresentationAreaContainer; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx index 8321daa07c..1a30a59523 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx @@ -104,7 +104,6 @@ export default function Whiteboard(props) { const language = mapLanguage(Settings?.application?.locale?.toLowerCase() || 'en'); const [currentTool, setCurrentTool] = React.useState(null); const [currentStyle, setCurrentStyle] = React.useState({}); - const [currentCameraPoint, setCurrentCameraPoint] = React.useState({}); const [isMoving, setIsMoving] = React.useState(false); const [isPanning, setIsPanning] = React.useState(shortcutPanning); const [panSelected, setPanSelected] = React.useState(isPanning); @@ -472,16 +471,12 @@ export default function Whiteboard(props) { } }, [tldrawAPI?.getPageState()?.camera, presentationWidth, presentationHeight]); - // change tldraw page when presentation page changes React.useEffect(() => { - if (tldrawAPI && curPageId && slidePosition) { - tldrawAPI.changePage(curPageId); - const newZoom = prevSlidePosition - ? calculateZoom(prevSlidePosition.viewBoxWidth, prevSlidePosition.viewBoxHeight) - : calculateZoom(slidePosition.viewBoxWidth, slidePosition.viewBoxHeight); - tldrawAPI?.setCamera([slidePosition.x, slidePosition.y], newZoom, 'zoomed_previous_page'); + if (isPresenter && slidePosition) { + const currentZoom = calculateZoom(slidePosition?.viewBoxWidth, slidePosition?.viewBoxHeight); + tldrawAPI?.setCamera([slidePosition?.x, slidePosition?.y], currentZoom); } - }, [curPageId]); + }, [slidePosition?.viewBoxWidth, slidePosition?.viewBoxHeight]); // change tldraw camera when slidePosition changes React.useEffect(() => { @@ -812,10 +807,6 @@ export default function Whiteboard(props) { if (reason && isPresenter && slidePosition && (reason.includes('zoomed') || reason.includes('panned'))) { const camera = tldrawAPI?.getPageState()?.camera; - const isForcePanning = tldrawAPI?.isForcePanning; - if (currentCameraPoint[curPageId] && !isPanning && !isForcePanning) { - camera.point = currentCameraPoint[curPageId]; - } // limit bounds if (tldrawAPI?.viewport.maxX > slidePosition.width) { @@ -831,6 +822,11 @@ export default function Whiteboard(props) { camera.point[1] = 0; } + if (camera.point[0] === 0 && camera.point[1] === 0) { + const newZoom = calculateZoom(slidePosition.viewBoxWidth, slidePosition.viewBoxHeight); + e?.setCamera([slidePosition.x, slidePosition.y], newZoom); + } + const zoomFitSlide = calculateZoom(slidePosition.width, slidePosition.height); if (camera.zoom < zoomFitSlide) { camera.zoom = zoomFitSlide; @@ -854,20 +850,13 @@ export default function Whiteboard(props) { viewedRegionH = HUNDRED_PERCENT; } - if (e?.currentPageId == curPageId) { - setCurrentCameraPoint({ - ...currentCameraPoint, - [e?.currentPageId]: camera?.point, - }) - } - zoomSlide( parseInt(curPageId, 10), podId, viewedRegionW, viewedRegionH, - currentCameraPoint[curPageId] ? currentCameraPoint[curPageId][0] : camera.point[0], - currentCameraPoint[curPageId] ? currentCameraPoint[curPageId][1] : camera.point[1], + camera.point[0], + camera.point[1], ); } // don't allow non-presenters to pan&zoom @@ -1006,16 +995,6 @@ export default function Whiteboard(props) { setCurrentStyle({ ...currentStyle, ...command?.after?.appState?.currentStyle }); } - if (command && command?.id?.includes('change_page')) { - const camera = tldrawAPI?.getPageState()?.camera; - if (currentCameraPoint[app?.currentPageId] && camera) { - tldrawAPI?.setCamera( - [currentCameraPoint[app?.currentPageId][0], currentCameraPoint[app?.currentPageId][1]], - camera?.zoom - ); - } - } - const changedShapes = command.after?.document?.pages[app.currentPageId]?.shapes; if (!isMounting && app.currentPageId !== curPageId) { // can happen then the "move to page action" is called, or using undo after changing a page diff --git a/bigbluebutton-tests/playwright/presentation/presentation.spec.js b/bigbluebutton-tests/playwright/presentation/presentation.spec.js index 9e9c633cef..b10241e464 100644 --- a/bigbluebutton-tests/playwright/presentation/presentation.spec.js +++ b/bigbluebutton-tests/playwright/presentation/presentation.spec.js @@ -52,11 +52,7 @@ test.describe.parallel('Presentation', () => { await presentation.hidePresentationToolbar(); }); - /** - * temporally skipped because it's currently failing the screenshot comparisons - * due to https://github.com/bigbluebutton/bigbluebutton/issues/18232 - */ - test.skip('Zoom In, Zoom Out, Reset Zoom @ci', async ({ browser, context, page }) => { + test('Zoom In, Zoom Out, Reset Zoom @ci', async ({ browser, context, page }) => { const presentation = new Presentation(browser, context); await presentation.initPages(page); await presentation.zoom();