diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala index f73d74a4e5537e794c42c7ce3635d841c1adf4d8..7ada8b233a477cc0f48955f28e14b102f16b8fa8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/WhiteboardModel.scala @@ -105,49 +105,65 @@ class WhiteboardModel extends SystemConfiguration { //not empty and head id equals annotation id //println("!usersAnnotations.isEmpty: " + (!usersAnnotations.isEmpty) + ", usersAnnotations.head.id == annotation.id: " + (usersAnnotations.head.id == annotation.id)); - if (!usersAnnotations.isEmpty && usersAnnotations.head.id == annotation.id) { - var dimensions: List[Int] = List[Int]() - annotation.annotationInfo.get("dimensions").foreach(d => { - d match { - case d2: List[_] => dimensions = convertListNumbersToInt(d2) + + var dimensions: List[Int] = List[Int]() + annotation.annotationInfo.get("dimensions").foreach(d => { + d match { + case d2: List[_] => dimensions = convertListNumbersToInt(d2) + } + }) + + //println("dimensions.size(): " + dimensions.size()); + if (dimensions.length == 2) { + var oldPoints: List[Float] = List[Float]() + val oldAnnotationOption: Option[AnnotationVO] = usersAnnotations.headOption + if (!oldAnnotationOption.isEmpty) { + val oldAnnotation = oldAnnotationOption.get + if (oldAnnotation.id == annotation.id) { + oldAnnotation.annotationInfo.get("points").foreach(a => { + a match { + case a2: List[_] => oldPoints = a2.asInstanceOf[List[Float]] + } + }) } - }) - - //println("dimensions.size(): " + dimensions.size()); - if (dimensions.length == 2) { - val oldAnnotation = usersAnnotations.head - var oldPoints: List[Float] = List[Float]() - oldAnnotation.annotationInfo.get("points").foreach(a => { - a match { - case a2: List[_] => oldPoints = a2.asInstanceOf[List[Float]] - } - }) - - var newPoints: List[Float] = List[Float]() - annotation.annotationInfo.get("points").foreach(a => { - a match { - case a2: List[_] => newPoints = convertListNumbersToFloat(a2) - } - }) //newPoints = a.asInstanceOf[ArrayList[Float]]) - - //println("oldPoints.size(): " + oldPoints.size()); - - //val oldPointsJava: java.util.List[java.lang.Float] = oldPoints.asJava.asInstanceOf[java.util.List[java.lang.Float]] - //println("****class = " + oldPointsJava.getClass()) - val pathData = BezierWrapper.lineSimplifyAndCurve((oldPoints ::: newPoints).asJava.asInstanceOf[java.util.List[java.lang.Float]], dimensions(0), dimensions(1)) - //println("Path data: pointssize " + pathData.points.size() + " commandssize " + pathData.commands.size()) - - val updatedAnnotationData = annotation.annotationInfo + ("points" -> pathData.points.asScala.toList) + ("commands" -> pathData.commands.asScala.toList) - val updatedAnnotation = annotation.copy(position = oldAnnotation.position, annotationInfo = updatedAnnotationData) - - val newAnnotationsMap = wb.annotationsMap + (userId -> (updatedAnnotation :: usersAnnotations.tail)) - //println("Annotation has position [" + usersAnnotations.head.position + "]") - val newWb = wb.copy(annotationsMap = newAnnotationsMap) - //println("Updating annotation on page [" + wb.id + "]. After numAnnotations=[" + getAnnotationsByUserId(wb, userId).length + "].") - saveWhiteboard(newWb) + } + + var newPoints: List[Float] = List[Float]() + annotation.annotationInfo.get("points").foreach(a => { + a match { + case a2: List[_] => newPoints = convertListNumbersToFloat(a2) + } + }) //newPoints = a.asInstanceOf[ArrayList[Float]]) + + //println("oldPoints.size(): " + oldPoints.size) + + //val oldPointsJava: java.util.List[java.lang.Float] = oldPoints.asJava.asInstanceOf[java.util.List[java.lang.Float]] + //println("****class = " + oldPointsJava.getClass()) + val pathData = BezierWrapper.lineSimplifyAndCurve((oldPoints ::: newPoints).asJava.asInstanceOf[java.util.List[java.lang.Float]], dimensions(0), dimensions(1)) + //println("Path data: pointssize " + pathData.points.size() + " commandssize " + pathData.commands.size()) + + val updatedAnnotationData = annotation.annotationInfo + ("points" -> pathData.points.asScala.toList) + ("commands" -> pathData.commands.asScala.toList) + //println("oldAnnotation value = " + oldAnnotationOption.getOrElse("Empty")) + + var newPosition: Int = oldAnnotationOption match { + case Some(annotation) => annotation.position + case None => wb.annotationCount + } + + val updatedAnnotation = annotation.copy(position = newPosition, annotationInfo = updatedAnnotationData) - rtnAnnotation = updatedAnnotation + var newUsersAnnotations: List[AnnotationVO] = oldAnnotationOption match { + case Some(annotation) => usersAnnotations.tail + case None => usersAnnotations } + + val newAnnotationsMap = wb.annotationsMap + (userId -> (updatedAnnotation :: newUsersAnnotations)) + //println("Annotation has position [" + usersAnnotations.head.position + "]") + val newWb = wb.copy(annotationsMap = newAnnotationsMap) + //println("Updating annotation on page [" + wb.id + "]. After numAnnotations=[" + getAnnotationsByUserId(wb, userId).length + "].") + saveWhiteboard(newWb) + + rtnAnnotation = updatedAnnotation } rtnAnnotation diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx index af7c7d4d5d44ae2599599a22eb2467767c693e71..0276a44e4aeb8b6f7d359d848295bc207c4f7aed 100755 --- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx @@ -481,6 +481,15 @@ class PresentationArea extends PureComponent { width, height, }} + published + whiteboardId={currentSlide.id} + /> + <AnnotationGroupContainer + {...{ + width, + height, + }} + published={false} whiteboardId={currentSlide.id} /> <CursorWrapperContainer diff --git a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx index 62fd2f003acd7edd452908d623280cdd640a9be7..862685841526701b027b127217b853eb7205297f 100755 --- a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx @@ -6,7 +6,7 @@ import GroupChat from '/imports/api/group-chat'; import Users from '/imports/api/users'; import Annotations from '/imports/api/annotations'; import AnnotationsTextService from '/imports/ui/components/whiteboard/annotations/text/service'; -import AnnotationsLocal from '/imports/ui/components/whiteboard/service'; +import { Annotations as AnnotationsLocal } from '/imports/ui/components/whiteboard/service'; const CHAT_CONFIG = Meteor.settings.public.chat; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/service.js index 4550a601599cec4dbd0602758c0442b54344150a..37e0ccf6a11a03833d6725612a1872dffb185151 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/service.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/service.js @@ -1,6 +1,6 @@ -import Annotations from '/imports/ui/components/whiteboard/service'; +import { UnsentAnnotations } from '/imports/ui/components/whiteboard/service'; -const getAnnotationById = _id => Annotations.findOne({ +const getAnnotationById = _id => UnsentAnnotations.findOne({ _id, }); diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/static-annotation/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/static-annotation/service.js index 4550a601599cec4dbd0602758c0442b54344150a..fb90fb58d5e3c3607821d70d45b555619b56763e 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/static-annotation/service.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/static-annotation/service.js @@ -1,4 +1,4 @@ -import Annotations from '/imports/ui/components/whiteboard/service'; +import { Annotations } from '/imports/ui/components/whiteboard/service'; const getAnnotationById = _id => Annotations.findOne({ _id, diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/container.jsx index 6bdaeb2f37bc688a5381bd3beca1d6dcec54eed9..5f9ca81d054e30d373dd1b649f1e384e6a928488 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/container.jsx @@ -4,18 +4,27 @@ import { withTracker } from 'meteor/react-meteor-data'; import AnnotationGroupService from './service'; import AnnotationGroup from './component'; -const AnnotationGroupContainer = props => ( +const AnnotationGroupContainer = ({ + annotationsInfo, width, height, whiteboardId, +}) => ( <AnnotationGroup - annotationsInfo={props.annotationsInfo} - slideWidth={props.width} - slideHeight={props.height} - whiteboardId={props.whiteboardId} + annotationsInfo={annotationsInfo} + slideWidth={width} + slideHeight={height} + whiteboardId={whiteboardId} /> ); export default withTracker((params) => { - const { whiteboardId } = params; - const annotationsInfo = AnnotationGroupService.getCurrentAnnotationsInfo(whiteboardId); + const { + whiteboardId, + published, + } = params; + + const fetchFunc = published + ? AnnotationGroupService.getCurrentAnnotationsInfo : AnnotationGroupService.getUnsetAnnotations; + + const annotationsInfo = fetchFunc(whiteboardId); return { annotationsInfo, }; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/service.js index e0428fe9d59c904b17c594048b4bd788b36105e6..683b8449a78a10d3333ae6c11b6a963e11339e2c 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/service.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-group/service.js @@ -1,4 +1,4 @@ -import Annotations from '/imports/ui/components/whiteboard/service'; +import { Annotations, UnsentAnnotations } from '/imports/ui/components/whiteboard/service'; const getCurrentAnnotationsInfo = (whiteboardId) => { if (!whiteboardId) { @@ -8,7 +8,22 @@ const getCurrentAnnotationsInfo = (whiteboardId) => { return Annotations.find( { whiteboardId, - // annotationType: { $ne: 'pencil_base' }, + }, + { + sort: { position: 1 }, + fields: { status: 1, _id: 1, annotationType: 1 }, + }, + ).fetch(); +}; + +const getUnsetAnnotations = (whiteboardId) => { + if (!whiteboardId) { + return null; + } + + return UnsentAnnotations.find( + { + whiteboardId, }, { sort: { position: 1 }, @@ -19,4 +34,5 @@ const getCurrentAnnotationsInfo = (whiteboardId) => { export default { getCurrentAnnotationsInfo, + getUnsetAnnotations, }; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx index 1da0de3b8b34c73e7919c3a164eacbf1b49853a5..01e9f7a415c9c9bfced872661fa1d542e7c32980 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx @@ -1,16 +1,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, getStrokeWidth, denormalizeCoord } from '../helpers'; export default class EllipseDrawComponent extends Component { - shouldComponentUpdate(nextProps) { - return this.props.version !== nextProps.version; + const { version } = this.props; + return version !== nextProps.version; } getCoordinates() { - const { points } = this.props.annotation; - const { slideWidth, slideHeight } = this.props; + const { slideWidth, slideHeight, annotation } = this.props; + const { points } = annotation; // x1 and y1 - coordinates of the ellipse's top left corner // x2 and y2 - coordinates of the ellipse's bottom right corner @@ -24,10 +24,10 @@ export default class EllipseDrawComponent extends Component { // cx and cy - coordinates of the ellipse's center let rx = (x2 - x1) / 2; let ry = (y2 - y1) / 2; - const cx = ((rx + x1) * slideWidth) / 100; - const cy = ((ry + y1) * slideHeight) / 100; - rx = Math.abs((rx / 100) * slideWidth); - ry = Math.abs((ry / 100) * slideHeight); + const cx = denormalizeCoord(rx + x1, slideWidth); + const cy = denormalizeCoord(ry + y1, slideHeight); + rx = denormalizeCoord(Math.abs(rx), slideWidth); + ry = denormalizeCoord(Math.abs(ry), slideHeight); return { cx, @@ -40,7 +40,9 @@ export default class EllipseDrawComponent extends Component { render() { const results = this.getCoordinates(); const { annotation, slideWidth } = this.props; - const { cx, cy, rx, ry } = results; + const { + cx, cy, rx, ry, + } = results; return ( <ellipse @@ -49,8 +51,8 @@ export default class EllipseDrawComponent extends Component { rx={rx} ry={ry} fill="none" - stroke={AnnotationHelpers.getFormattedColor(annotation.color)} - strokeWidth={AnnotationHelpers.getStrokeWidth(annotation.thickness, slideWidth)} + stroke={getFormattedColor(annotation.color)} + strokeWidth={getStrokeWidth(annotation.thickness, slideWidth)} style={{ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)' }} /> ); diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/helpers.js b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/helpers.js index 7c4ff2a769ee0d2171ba9061b7b2ae6b74e0c259..5d67acd6fe8becf44e48807d4dafee0ca0ba5ccc 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/helpers.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/helpers.js @@ -20,7 +20,10 @@ const getFormattedColor = (color) => { const getStrokeWidth = (thickness, slideWidth) => (thickness * slideWidth) / 100; -export default { +const denormalizeCoord = (normCoord, sideLength) => ((normCoord / 100) * sideLength).toFixed(2); + +export { getFormattedColor, getStrokeWidth, + denormalizeCoord, }; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/line/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/line/component.jsx index 81e8dacf9d8748637fde60f1647501eec3d96c5b..18725c6d909055090afad6b807f9f9c73305de6f 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/line/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/line/component.jsx @@ -1,21 +1,21 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, getStrokeWidth, denormalizeCoord } from '../helpers'; export default class LineDrawComponent extends Component { - shouldComponentUpdate(nextProps) { - return this.props.version !== nextProps.version; + const { version } = this.props; + return version !== nextProps.version; } getCoordinates() { - const { slideWidth, slideHeight } = this.props; - const { points } = this.props.annotation; + const { slideWidth, slideHeight, annotation } = this.props; + const { points } = annotation; - const x1 = (points[0] / 100) * slideWidth; - const y1 = (points[1] / 100) * slideHeight; - const x2 = (points[2] / 100) * slideWidth; - const y2 = (points[3] / 100) * slideHeight; + const x1 = denormalizeCoord(points[0], slideWidth); + const y1 = denormalizeCoord(points[1], slideHeight); + const x2 = denormalizeCoord(points[2], slideWidth); + const y2 = denormalizeCoord(points[3], slideHeight); return { x1, @@ -28,7 +28,9 @@ export default class LineDrawComponent extends Component { render() { const results = this.getCoordinates(); const { annotation, slideWidth } = this.props; - const { x1, y1, x2, y2 } = results; + const { + x1, y1, x2, y2, + } = results; return ( <line @@ -36,9 +38,9 @@ export default class LineDrawComponent extends Component { y1={y1} x2={x2} y2={y2} - stroke={AnnotationHelpers.getFormattedColor(annotation.color)} + stroke={getFormattedColor(annotation.color)} strokeLinejoin="round" - strokeWidth={AnnotationHelpers.getStrokeWidth(annotation.thickness, slideWidth)} + strokeWidth={getStrokeWidth(annotation.thickness, slideWidth)} style={{ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)' }} /> ); diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx index e367e04fb82b0917bd14b579f1f91d9dfbbafdfc..3a808503eae96addbe86d87827d5220f57649657 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/pencil/component.jsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, getStrokeWidth, denormalizeCoord } from '../helpers'; export default class PencilDrawComponent extends Component { static getInitialCoordinates(annotation, slideWidth, slideHeight) { @@ -8,11 +8,11 @@ export default class PencilDrawComponent extends Component { let i = 2; let path = ''; if (points && points.length >= 2) { - path = `M${(points[0] / 100) * slideWidth - }, ${(points[1] / 100) * slideHeight}`; + path = `M${denormalizeCoord(points[0], slideWidth) + }, ${denormalizeCoord(points[1], slideHeight)}`; while (i < points.length) { - path = `${path} L${(points[i] / 100) * slideWidth - }, ${(points[i + 1] / 100) * slideHeight}`; + path = `${path} L${denormalizeCoord(points[i], slideWidth) + }, ${denormalizeCoord(points[i + 1], slideHeight)}`; i += 2; } } @@ -30,32 +30,32 @@ export default class PencilDrawComponent extends Component { switch (commands[i]) { // MOVE_TO - consumes 1 pair of values case 1: - path = `${path} M${(points[j] / 100) * slideWidth} ${(points[j + 1] / 100) * slideHeight}`; + path = `${path} M${denormalizeCoord(points[j], slideWidth)} ${denormalizeCoord(points[j + 1], slideHeight)}`; j += 2; break; // LINE_TO - consumes 1 pair of values case 2: - path = `${path} L${(points[j] / 100) * slideWidth} ${(points[j + 1] / 100) * slideHeight}`; + path = `${path} L${denormalizeCoord(points[j], slideWidth)} ${denormalizeCoord(points[j + 1], slideHeight)}`; j += 2; break; // QUADRATIC_CURVE_TO - consumes 2 pairs of values // 1st pair is a control point, second is a coordinate case 3: - path = `${path} Q${(points[j] / 100) * slideWidth}, ${ - (points[j + 1] / 100) * slideHeight}, ${(points[j + 2] / 100) * slideWidth}, ${ - (points[j + 3] / 100) * slideHeight}`; + path = `${path} Q${denormalizeCoord(points[j], slideWidth)}, ${ + denormalizeCoord(points[j + 1], slideHeight)}, ${denormalizeCoord(points[j + 2], slideWidth)}, ${ + denormalizeCoord(points[j + 3], slideHeight)}`; j += 4; break; // CUBIC_CURVE_TO - consumes 3 pairs of values // 1st and 2nd are control points, 3rd is an end coordinate case 4: - path = `${path} C${(points[j] / 100) * slideWidth}, ${ - (points[j + 1] / 100) * slideHeight}, ${(points[j + 2] / 100) * slideWidth}, ${ - (points[j + 3] / 100) * slideHeight}, ${(points[j + 4] / 100) * slideWidth}, ${ - (points[j + 5] / 100) * slideHeight}`; + path = `${path} C${denormalizeCoord(points[j], slideWidth)}, ${ + denormalizeCoord(points[j + 1], slideHeight)}, ${denormalizeCoord(points[j + 2], slideWidth)}, ${ + denormalizeCoord(points[j + 3], slideHeight)}, ${denormalizeCoord(points[j + 4], slideWidth)}, ${ + denormalizeCoord(points[j + 5], slideHeight)}`; j += 6; break; @@ -67,7 +67,7 @@ export default class PencilDrawComponent extends Component { // If that's just one coordinate at the end (dot) - we want to display it. // So adding L with the same X and Y values to the path if (path && points.length === 2) { - path = `${path} L${(points[0] / 100) * slideWidth} ${(points[1] / 100) * slideHeight}`; + path = `${path} L${denormalizeCoord(points[0], slideWidth)} ${denormalizeCoord(points[1], slideHeight)}`; } return { path, points }; @@ -85,13 +85,17 @@ export default class PencilDrawComponent extends Component { } shouldComponentUpdate(nextProps) { - return this.props.version !== nextProps.version; + const { version } = this.props; + return version !== nextProps.version; } componentWillUpdate(nextProps) { - const { annotation, slideWidth, slideHeight } = nextProps; - if (annotation.points.length !== this.props.annotation.points.length) { - this.path = this.getCoordinates(annotation, slideWidth, slideHeight); + const { annotation: nextAnnotation, slideWidth, slideHeight } = nextProps; + const { points: nextPoints } = nextAnnotation; + const { annotation } = this.props; + const { points } = annotation; + if (nextPoints.length !== points.length) { + this.path = this.getCoordinates(nextAnnotation, slideWidth, slideHeight); } } @@ -123,14 +127,15 @@ export default class PencilDrawComponent extends Component { updateCoordinates(annotation, slideWidth, slideHeight) { const { points } = annotation; + let i = this.points.length; let path = ''; - while (i < points.length) { - path = `${path} L${(points[i] / 100) * slideWidth - }, ${(points[i + 1] / 100) * slideHeight}`; + path = `${path} L${denormalizeCoord(points[i], slideWidth) + }, ${denormalizeCoord(points[i + 1], slideHeight)}`; i += 2; } + path = this.path + path; return { path, points }; @@ -141,9 +146,9 @@ export default class PencilDrawComponent extends Component { return ( <path fill="none" - stroke={AnnotationHelpers.getFormattedColor(annotation.color)} + stroke={getFormattedColor(annotation.color)} d={this.getCurrentPath()} - strokeWidth={AnnotationHelpers.getStrokeWidth(annotation.thickness, slideWidth)} + strokeWidth={getStrokeWidth(annotation.thickness, slideWidth)} strokeLinejoin="round" strokeLinecap="round" style={{ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)' }} diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/rectangle/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/rectangle/component.jsx index 79b6284666ebb7bd07c5a086e6d57de8d109f853..364f0cb47a32a5ba85446f8c0079d437d447fc87 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/rectangle/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/rectangle/component.jsx @@ -1,16 +1,17 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, getStrokeWidth, denormalizeCoord } from '../helpers'; export default class RectangleDrawComponent extends Component { - shouldComponentUpdate(nextProps) { - return this.props.version !== nextProps.version; + const { version } = this.props; + return version !== nextProps.version; } getCoordinates() { - const { points } = this.props.annotation; - const { slideWidth, slideHeight } = this.props; + const { slideWidth, slideHeight, annotation } = this.props; + const { points } = annotation; + /* eslint-disable prefer-destructuring */ // x1 and y1 are the coordinates of the top left corner of the annotation // x2 and y2 are the coordinates of the bottom right corner of the annotation let x1 = points[0]; @@ -29,11 +30,11 @@ export default class RectangleDrawComponent extends Component { y1 = points[3]; y2 = points[1]; } - - const x = (x1 / 100) * slideWidth; - const y = (y1 / 100) * slideHeight; - const width = ((x2 - x1) / 100) * slideWidth; - const height = ((y2 - y1) / 100) * slideHeight; + /* eslint-enable prefer-destructuring */ + const x = denormalizeCoord(x1, slideWidth); + const y = denormalizeCoord(y1, slideHeight); + const width = denormalizeCoord((x2 - x1), slideWidth); + const height = denormalizeCoord((y2 - y1), slideHeight); return { x, @@ -54,8 +55,8 @@ export default class RectangleDrawComponent extends Component { width={results.width} height={results.height} fill="none" - stroke={AnnotationHelpers.getFormattedColor(annotation.color)} - strokeWidth={AnnotationHelpers.getStrokeWidth(annotation.thickness, slideWidth)} + stroke={getFormattedColor(annotation.color)} + strokeWidth={getStrokeWidth(annotation.thickness, slideWidth)} style={{ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)' }} /> ); diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx index 3cafe4d3fbc5bfb442d0a60c9d8254e5b36fe9f3..7c87acc2b2addc521c6ba296d555ddf5afcbe2ab 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import RenderInBrowser from 'react-render-in-browser'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, denormalizeCoord } from '../helpers'; const DRAW_END = Meteor.settings.public.whiteboard.annotations.status.end; @@ -109,11 +109,11 @@ export default class TextDrawComponent extends Component { text, } = annotation; - const _x = (x / 100) * slideWidth; - const _y = (y / 100) * slideHeight; - const _width = (textBoxWidth / 100) * slideWidth; - const _height = (textBoxHeight / 100) * slideHeight; - const _fontColor = AnnotationHelpers.getFormattedColor(fontColor); + const _x = denormalizeCoord(x, slideWidth); + const _y = denormalizeCoord(y, slideHeight); + const _width = denormalizeCoord(textBoxWidth, slideWidth); + const _height = denormalizeCoord(textBoxHeight, slideHeight); + const _fontColor = getFormattedColor(fontColor); const _fontSize = fontSize; const _calcedFontSize = (calcedFontSize / 100) * slideHeight; const _text = text; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx index 06e49c1e86e840a2a990b81ab663e1c3b8478347..2a2133def3add6c431195f421c199aecce20f297 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx @@ -1,16 +1,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import AnnotationHelpers from '../helpers'; +import { getFormattedColor, getStrokeWidth, denormalizeCoord } from '../helpers'; export default class TriangleDrawComponent extends Component { - shouldComponentUpdate(nextProps) { - return this.props.version !== nextProps.version; + const { version } = this.props; + return version !== nextProps.version; } getCoordinates() { - const { slideWidth, slideHeight } = this.props; - const { points } = this.props.annotation; + const { slideWidth, slideHeight, annotation } = this.props; + const { points } = annotation; // points[0] and points[1] are x and y coordinates of the top left corner of the annotation // points[2] and points[3] are x and y coordinates of the bottom right corner of the annotation @@ -21,13 +21,13 @@ export default class TriangleDrawComponent extends Component { const xTop = ((xBottomRight - xBottomLeft) / 2) + xBottomLeft; const yTop = points[1]; - const path = `M${(xTop / 100) * slideWidth - },${(yTop / 100) * slideHeight - },${(xBottomLeft / 100) * slideWidth - },${(yBottomLeft / 100) * slideHeight - },${(xBottomRight / 100) * slideWidth - },${(yBottomRight / 100) * slideHeight - }Z`; + const path = `M${denormalizeCoord(xTop, slideWidth) + },${denormalizeCoord(yTop, slideHeight) + },${denormalizeCoord(xBottomLeft, slideWidth) + },${denormalizeCoord(yBottomLeft, slideHeight) + },${denormalizeCoord(xBottomRight, slideWidth) + },${denormalizeCoord(yBottomRight, slideHeight) + }Z`; return path; } @@ -39,9 +39,9 @@ export default class TriangleDrawComponent extends Component { <path style={{ WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)' }} fill="none" - stroke={AnnotationHelpers.getFormattedColor(annotation.color)} + stroke={getFormattedColor(annotation.color)} d={path} - strokeWidth={AnnotationHelpers.getStrokeWidth(annotation.thickness, slideWidth)} + strokeWidth={getStrokeWidth(annotation.thickness, slideWidth)} strokeLinejoin="miter" /> ); diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/service.js index f8712a3e36576d430bd50bb52fb8dc6b880848d9..4fa4bfe9edf6112de3c80f14958ed8fae0ab6158 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/service.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/service.js @@ -2,25 +2,25 @@ import Users from '/imports/api/users'; import Auth from '/imports/ui/services/auth'; import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user/'; import addAnnotationQuery from '/imports/api/annotations/addAnnotation'; -import logger from '/imports/startup/client/logger'; import { makeCall } from '/imports/ui/services/api'; -import { isEqual } from 'lodash'; const Annotations = new Mongo.Collection(null); +const UnsentAnnotations = new Mongo.Collection(null); const ANNOTATION_CONFIG = Meteor.settings.public.whiteboard.annotations; -const DRAW_START = ANNOTATION_CONFIG.status.start; +const DRAW_UPDATE = ANNOTATION_CONFIG.status.update; const DRAW_END = ANNOTATION_CONFIG.status.end; -const discardedList = []; + +const ANNOTATION_TYPE_PENCIL = 'pencil'; let annotationsStreamListener = null; -export function addAnnotationToDiscardedList(annotation) { - if (!discardedList.includes(annotation)) discardedList.push(annotation); -} +const clearPreview = (annotation) => { + UnsentAnnotations.remove({ id: annotation }); +}; function clearFakeAnnotations() { - Annotations.remove({ id: /-fake/g }); + UnsentAnnotations.remove({}); } function handleAddedAnnotation({ @@ -29,55 +29,11 @@ function handleAddedAnnotation({ const isOwn = Auth.meetingID === meetingId && Auth.userID === userId; const query = addAnnotationQuery(meetingId, whiteboardId, userId, annotation); - if (!isOwn) { - Annotations.upsert(query.selector, query.modifier); - return; - } - - const fakeAnnotation = Annotations.findOne({ id: `${annotation.id}-fake` }); - let fakePoints; + Annotations.upsert(query.selector, query.modifier); - if (fakeAnnotation) { - fakePoints = fakeAnnotation.annotationInfo.points; - const { points: lastPoints } = annotation.annotationInfo; - - if (annotation.annotationType !== 'pencil') { - Annotations.update(fakeAnnotation._id, { - $set: { - position: annotation.position, - 'annotationInfo.color': isEqual(fakePoints, lastPoints) || annotation.status === DRAW_END - ? annotation.annotationInfo.color : fakeAnnotation.annotationInfo.color, - }, - $inc: { version: 1 }, // TODO: Remove all this version stuff - }); - return; - } + if (isOwn) { + UnsentAnnotations.remove({ id: `${annotation.id}` }); } - - Annotations.upsert(query.selector, query.modifier, (err) => { - if (err) { - logger.error({ - logCode: 'whiteboard_annotation_upsert_error', - extraInfo: { error: err }, - }, 'Error on adding an annotation'); - return; - } - - // Remove fake annotation for pencil on draw end - if (annotation.status === DRAW_END) { - Annotations.remove({ id: `${annotation.id}-fake` }); - return; - } - - if (annotation.status === DRAW_START) { - Annotations.update(fakeAnnotation._id, { - $set: { - position: annotation.position - 1, - }, - $inc: { version: 1 }, // TODO: Remove all this version stuff - }); - } - }); } function handleRemovedAnnotation({ @@ -85,14 +41,12 @@ function handleRemovedAnnotation({ }) { const query = { meetingId, whiteboardId }; - addAnnotationToDiscardedList(shapeId); - if (userId) { query.userId = userId; } if (shapeId) { - query.id = { $in: [shapeId, `${shapeId}-fake`] }; + query.id = shapeId; } Annotations.remove(query); @@ -109,7 +63,7 @@ export function initAnnotationsStreamListener() { const startStreamHandlersPromise = new Promise((resolve) => { const checkStreamHandlersInterval = setInterval(() => { const streamHandlersSize = Object.values(Meteor.StreamerCentral.instances[`annotations-${Auth.meetingID}`].handlers) - .filter(el => el != undefined) + .filter(el => el !== undefined) .length; if (!streamHandlersSize) { @@ -122,10 +76,7 @@ export function initAnnotationsStreamListener() { annotationsStreamListener.on('removed', handleRemovedAnnotation); annotationsStreamListener.on('added', ({ annotations }) => { - // Call handleAddedAnnotation when this annotation is not in discardedList - annotations - .filter(({ annotation }) => !discardedList.includes(annotation.id)) - .forEach(annotation => handleAddedAnnotation(annotation)); + annotations.forEach(annotation => handleAddedAnnotation(annotation)); }); }); } @@ -172,41 +123,52 @@ const proccessAnnotationsQueue = async () => { const annotations = annotationsQueue.splice(0, queueSize); // console.log('annotationQueue.length', annotationsQueue, annotationsQueue.length); - await makeCall('sendBulkAnnotations', annotations.filter(({ id }) => !discardedList.includes(id))); + await makeCall('sendBulkAnnotations', annotations); // ask tiago const delayPerc = Math.min(annotationsMaxDelayQueueSize, queueSize) / annotationsMaxDelayQueueSize; const delayDelta = annotationsBufferTimeMax - annotationsBufferTimeMin; const delayTime = annotationsBufferTimeMin + (delayDelta * delayPerc); + // console.log("delayPerc:", delayPerc) setTimeout(proccessAnnotationsQueue, delayTime); }; -export function sendAnnotation(annotation) { +const sendAnnotation = (annotation) => { // Prevent sending annotations while disconnected + // TODO: Change this to add the annotation, but delay the send until we're + // reconnected. With this it will miss things if (!Meteor.status().connected) return; - annotationsQueue.push(annotation); - if (!annotationsSenderIsRunning) setTimeout(proccessAnnotationsQueue, annotationsBufferTimeMin); - - // skip optimistic for draw end since the smoothing is done in akka - if (annotation.status === DRAW_END) return; - - const { position, ...relevantAnotation } = annotation; - const queryFake = addAnnotationQuery( - Auth.meetingID, annotation.wbId, Auth.userID, - { - ...relevantAnotation, - id: `${annotation.id}-fake`, - position: Number.MAX_SAFE_INTEGER, - annotationInfo: { - ...annotation.annotationInfo, - color: increaseBrightness(annotation.annotationInfo.color, 40), + if (annotation.status === DRAW_END) { + annotationsQueue.push(annotation); + if (!annotationsSenderIsRunning) setTimeout(proccessAnnotationsQueue, annotationsBufferTimeMin); + } else { + const { position, ...relevantAnotation } = annotation; + const queryFake = addAnnotationQuery( + Auth.meetingID, annotation.wbId, Auth.userID, + { + ...relevantAnotation, + id: `${annotation.id}`, + position: Number.MAX_SAFE_INTEGER, + annotationInfo: { + ...annotation.annotationInfo, + color: increaseBrightness(annotation.annotationInfo.color, 40), + }, }, - }, - ); + ); + + // This is a really hacky solution, but because of the previous code reuse we need to edit + // the pencil draw update modifier so that it sets the whole array instead of pushing to + // the end + const { status, annotationType } = relevantAnotation; + if (status === DRAW_UPDATE && annotationType === ANNOTATION_TYPE_PENCIL) { + delete queryFake.modifier.$push; + queryFake.modifier.$set['annotationInfo.points'] = annotation.annotationInfo.points; + } - Annotations.upsert(queryFake.selector, queryFake.modifier); -} + UnsentAnnotations.upsert(queryFake.selector, queryFake.modifier); + } +}; WhiteboardMultiUser.find({ meetingId: Auth.meetingID }).observeChanges({ changed: clearFakeAnnotations, @@ -218,4 +180,9 @@ Users.find({ userId: Auth.userID }, { fields: { presenter: 1 } }).observeChanges }, }); -export default Annotations; +export { + Annotations, + UnsentAnnotations, + sendAnnotation, + clearPreview, +}; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/component.jsx index 09cce8e8329b7ee079eb7d8035427678cd2783da..0ad127fd71b2e6b348b9a0c287d991b5ad58d483 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/component.jsx @@ -201,8 +201,7 @@ export default class WhiteboardOverlay extends Component { resetTextShapeSession, setTextShapeActiveId, contextMenuHandler, - addAnnotationToDiscardedList, - undoAnnotation, + clearPreview, updateCursor, } = this.props; @@ -218,8 +217,7 @@ export default class WhiteboardOverlay extends Component { resetTextShapeSession, setTextShapeActiveId, contextMenuHandler, - addAnnotationToDiscardedList, - undoAnnotation, + clearPreview, }; return ( @@ -257,6 +255,8 @@ WhiteboardOverlay.propTypes = { viewBoxHeight: PropTypes.number.isRequired, // Defines a handler to publish an annotation to the server sendAnnotation: PropTypes.func.isRequired, + // Defines a handler to clear a shape preview + clearPreview: PropTypes.func.isRequired, // Defines a current whiteboard id whiteboardId: PropTypes.string.isRequired, // Defines an object containing current settings for drawing diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/container.jsx index 86b1e08959a27939cb62caa61ed8c836a678cac5..90eef160d4a5a4d6a7e5fbe89fc96486b0c591b4 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/container.jsx @@ -2,7 +2,6 @@ import React from 'react'; import { withTracker } from 'meteor/react-meteor-data'; import PropTypes from 'prop-types'; import WhiteboardOverlayService from './service'; -import WhiteboardToolbarService from '../whiteboard-toolbar/service'; import WhiteboardOverlay from './component'; const WhiteboardOverlayContainer = (props) => { @@ -16,10 +15,9 @@ const WhiteboardOverlayContainer = (props) => { }; export default withTracker(() => ({ - undoAnnotation: WhiteboardToolbarService.undoAnnotation, + clearPreview: WhiteboardOverlayService.clearPreview, contextMenuHandler: WhiteboardOverlayService.contextMenuHandler, sendAnnotation: WhiteboardOverlayService.sendAnnotation, - addAnnotationToDiscardedList: WhiteboardOverlayService.addAnnotationToDiscardedList, setTextShapeActiveId: WhiteboardOverlayService.setTextShapeActiveId, resetTextShapeSession: WhiteboardOverlayService.resetTextShapeSession, drawSettings: WhiteboardOverlayService.getWhiteboardToolbarValues(), diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/pencil-draw-listener/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/pencil-draw-listener/component.jsx index 4caf3d09f16b48fba5c1cebe95d13cfd4548578e..68d8092fff7ade24849bbb6b1350b420a3c16f1c 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/pencil-draw-listener/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/pencil-draw-listener/component.jsx @@ -8,7 +8,7 @@ const DRAW_END = ANNOTATION_CONFIG.status.end; // maximum value of z-index to prevent other things from overlapping const MAX_Z_INDEX = (2 ** 31) - 1; -const POINTS_TO_BUFFER = 5; +const POINTS_TO_BUFFER = 2; export default class PencilDrawListener extends Component { constructor() { @@ -64,8 +64,8 @@ export default class PencilDrawListener extends Component { transformedSvgPoint = svgCoordinateToPercentages(transformedSvgPoint); // sending the first message - const _points = [transformedSvgPoint.x, transformedSvgPoint.y]; - this.handleDrawPencil(_points, DRAW_START, generateNewShapeId()); + this.points = [transformedSvgPoint.x, transformedSvgPoint.y]; + this.handleDrawPencil(this.points, DRAW_START, generateNewShapeId()); } commonDrawMoveHandler(clientX, clientY) { @@ -147,7 +147,6 @@ export default class PencilDrawListener extends Component { // if you switch to a different window using Alt+Tab while mouse is down and release it // it wont catch mouseUp and will keep tracking the movements. Thus we need this check. } else if (isRightClick) { - this.sendLastMessage(); this.discardAnnotation(); } } @@ -171,7 +170,6 @@ export default class PencilDrawListener extends Component { const { getCurrentShapeId } = actions; this.handleDrawPencil(this.points, DRAW_UPDATE, getCurrentShapeId()); - this.points = []; } } @@ -254,19 +252,16 @@ export default class PencilDrawListener extends Component { discardAnnotation() { const { - whiteboardId, actions, } = this.props; const { getCurrentShapeId, - addAnnotationToDiscardedList, - undoAnnotation, + clearPreview, } = actions; - - undoAnnotation(whiteboardId); - addAnnotationToDiscardedList(getCurrentShapeId()); + this.resetState(); + clearPreview(getCurrentShapeId()); } render() { diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/service.js index 95a0eb272af6c3938c0e9bd052c2ea19a9f6ebae..534ffe471ba0bbcdb326cc30b3872f2b0803e13d 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/service.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/service.js @@ -1,6 +1,6 @@ import Storage from '/imports/ui/services/storage/session'; import Auth from '/imports/ui/services/auth'; -import { sendAnnotation, addAnnotationToDiscardedList } from '/imports/ui/components/whiteboard/service'; +import { sendAnnotation, clearPreview } from '/imports/ui/components/whiteboard/service'; import { publishCursorUpdate } from '/imports/ui/components/cursor/service'; const DRAW_SETTINGS = 'drawSettings'; @@ -55,7 +55,6 @@ const updateCursor = (payload) => { }; export default { - addAnnotationToDiscardedList, sendAnnotation, getWhiteboardToolbarValues, setTextShapeActiveId, @@ -63,4 +62,5 @@ export default { getCurrentUserId, contextMenuHandler, updateCursor, + clearPreview, }; diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/shape-draw-listener/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/shape-draw-listener/component.jsx index 554c2469a8d9d76ae54d62c74c6c6d23371cc48d..5b70a3a73bfd3d124b6422220fd6afaea659e5ac 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/shape-draw-listener/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/shape-draw-listener/component.jsx @@ -174,8 +174,6 @@ export default class ShapeDrawListener extends Component { // if you switch to a different window using Alt+Tab while mouse is down and release it // it wont catch mouseUp and will keep tracking the movements. Thus we need this check. } else if (isRightClick) { - // this.isDrawing = false; - this.sendLastMessage(); this.discardAnnotation(); } } @@ -324,18 +322,16 @@ export default class ShapeDrawListener extends Component { discardAnnotation() { const { - whiteboardId, actions, } = this.props; const { getCurrentShapeId, - addAnnotationToDiscardedList, - undoAnnotation, + clearPreview, } = actions; - undoAnnotation(whiteboardId); - addAnnotationToDiscardedList(getCurrentShapeId()); + this.resetState(); + clearPreview(getCurrentShapeId()); } render() { diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx index 518c3aeea8f7ae72234145ac3834287b144d60cf..056d81ce9772d52b506feffbb74d36af8f7f0eff 100755 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-overlay/text-draw-listener/component.jsx @@ -215,12 +215,11 @@ export default class TextDrawListener extends Component { } // second case is when a user finished writing the text and publishes the final result + } else if (isRightClick) { + this.discardAnnotation(); } else { // publishing the final shape and resetting the state this.sendLastMessage(); - if (isRightClick) { - this.discardAnnotation(); - } } } @@ -460,18 +459,16 @@ export default class TextDrawListener extends Component { discardAnnotation() { const { - whiteboardId, actions, } = this.props; const { getCurrentShapeId, - addAnnotationToDiscardedList, - undoAnnotation, + clearPreview, } = actions; - undoAnnotation(whiteboardId); - addAnnotationToDiscardedList(getCurrentShapeId()); + this.resetState(); + clearPreview(getCurrentShapeId()); } render() {