Merge pull request #9019 from capilkey/2.3-simple-wb
Whiteboard performance improvements
This commit is contained in:
commit
df40e6b214
@ -105,7 +105,7 @@ 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 {
|
||||
@ -115,13 +115,18 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
|
||||
//println("dimensions.size(): " + dimensions.size());
|
||||
if (dimensions.length == 2) {
|
||||
val oldAnnotation = usersAnnotations.head
|
||||
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]]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var newPoints: List[Float] = List[Float]()
|
||||
annotation.annotationInfo.get("points").foreach(a => {
|
||||
@ -130,7 +135,7 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
}
|
||||
}) //newPoints = a.asInstanceOf[ArrayList[Float]])
|
||||
|
||||
//println("oldPoints.size(): " + oldPoints.size());
|
||||
//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())
|
||||
@ -138,9 +143,21 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
//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)
|
||||
//println("oldAnnotation value = " + oldAnnotationOption.getOrElse("Empty"))
|
||||
|
||||
val newAnnotationsMap = wb.annotationsMap + (userId -> (updatedAnnotation :: usersAnnotations.tail))
|
||||
var newPosition: Int = oldAnnotationOption match {
|
||||
case Some(annotation) => annotation.position
|
||||
case None => wb.annotationCount
|
||||
}
|
||||
|
||||
val updatedAnnotation = annotation.copy(position = newPosition, annotationInfo = updatedAnnotationData)
|
||||
|
||||
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 + "].")
|
||||
@ -148,7 +165,6 @@ class WhiteboardModel extends SystemConfiguration {
|
||||
|
||||
rtnAnnotation = updatedAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
rtnAnnotation
|
||||
}
|
||||
|
@ -489,6 +489,15 @@ class PresentationArea extends PureComponent {
|
||||
width,
|
||||
height,
|
||||
}}
|
||||
published
|
||||
whiteboardId={currentSlide.id}
|
||||
/>
|
||||
<AnnotationGroupContainer
|
||||
{...{
|
||||
width,
|
||||
height,
|
||||
}}
|
||||
published={false}
|
||||
whiteboardId={currentSlide.id}
|
||||
/>
|
||||
<CursorWrapperContainer
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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)' }}
|
||||
/>
|
||||
);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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)' }}
|
||||
/>
|
||||
);
|
||||
|
@ -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)' }}
|
||||
|
@ -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)' }}
|
||||
/>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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,12 +21,12 @@ 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
|
||||
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"
|
||||
/>
|
||||
);
|
||||
|
@ -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;
|
||||
|
||||
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,31 +123,32 @@ 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;
|
||||
|
||||
if (annotation.status === DRAW_END) {
|
||||
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;
|
||||
|
||||
} else {
|
||||
const { position, ...relevantAnotation } = annotation;
|
||||
const queryFake = addAnnotationQuery(
|
||||
Auth.meetingID, annotation.wbId, Auth.userID,
|
||||
{
|
||||
...relevantAnotation,
|
||||
id: `${annotation.id}-fake`,
|
||||
id: `${annotation.id}`,
|
||||
position: Number.MAX_SAFE_INTEGER,
|
||||
annotationInfo: {
|
||||
...annotation.annotationInfo,
|
||||
@ -205,8 +157,18 @@ export function sendAnnotation(annotation) {
|
||||
},
|
||||
);
|
||||
|
||||
Annotations.upsert(queryFake.selector, queryFake.modifier);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user