Merge pull request #18452 from KDSBrowne/bbb-18232

fix: Correct Slide Positioning After Zoom and Sync During Presentation Change
This commit is contained in:
Anton Georgiev 2023-08-08 19:08:42 -04:00 committed by GitHub
commit 588bf5c2b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 39 additions and 55 deletions

View File

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

View File

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

View File

@ -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}
/>
</Styled.ActionsBar>
);
@ -517,6 +524,8 @@ class App extends Component {
darkTheme,
} = this.props;
const { presentationFitToWidth } = this.state;
return (
<>
<Notifications />
@ -540,7 +549,7 @@ class App extends Component {
<NavBarContainer main="new" />
<NewWebcamContainer isLayoutSwapped={!presentationIsOpen} />
<Styled.TextMeasure id="text-measure" />
{shouldShowPresentation ? <PresentationAreaContainer darkTheme={darkTheme} presentationIsOpen={presentationIsOpen} /> : null}
{shouldShowPresentation ? <PresentationAreaContainer setPresentationFitToWidth={this.setPresentationFitToWidth} fitToWidth={presentationFitToWidth} darkTheme={darkTheme} presentationIsOpen={presentationIsOpen} /> : null}
{shouldShowScreenshare ? <ScreenshareContainer isLayoutSwapped={!presentationIsOpen} /> : null}
{
shouldShowExternalVideo

View File

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

View File

@ -7,13 +7,15 @@ const PresentationArea = ({
height,
presentationIsOpen,
darkTheme,
setPresentationFitToWidth,
fitToWidth,
}) => {
const presentationAreaSize = {
presentationAreaWidth: width,
presentationAreaHeight: height,
};
return (
<PresentationPodsContainer {...{ presentationAreaSize, presentationIsOpen, darkTheme }} />
<PresentationPodsContainer {...{ presentationAreaSize, presentationIsOpen, darkTheme, setPresentationFitToWidth, fitToWidth }} />
);
};

View File

@ -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 <PresentationArea {...{ ...presentation, presentationIsOpen, darkTheme }} />;
return <PresentationArea {...{ ...presentation, presentationIsOpen, darkTheme, setPresentationFitToWidth, fitToWidth }} />;
};
export default PresentationAreaContainer;

View File

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

View File

@ -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();