2016-11-19 01:35:28 +08:00
|
|
|
import { check } from 'meteor/check';
|
|
|
|
import Logger from '/imports/startup/server/logger';
|
2017-10-12 10:00:28 +08:00
|
|
|
import Annotations from '/imports/api/annotations';
|
2018-04-06 04:19:41 +08:00
|
|
|
import RedisPubSub from '/imports/startup/server/redis';
|
2017-08-31 07:12:03 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
const ANNOTATION_TYPE_TEXT = 'text';
|
|
|
|
const ANNOTATION_TYPE_PENCIL = 'pencil';
|
2016-11-19 01:35:28 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// line, triangle, ellipse, rectangle
|
|
|
|
function handleCommonAnnotation(meetingId, whiteboardId, userId, annotation) {
|
2018-01-08 08:25:56 +08:00
|
|
|
const {
|
|
|
|
id, status, annotationType, annotationInfo, wbId, position,
|
|
|
|
} = annotation;
|
2016-11-19 01:35:28 +08:00
|
|
|
|
|
|
|
const selector = {
|
|
|
|
meetingId,
|
2017-10-12 08:52:57 +08:00
|
|
|
id,
|
|
|
|
userId,
|
2016-11-19 01:35:28 +08:00
|
|
|
};
|
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
const modifier = {
|
2016-11-19 01:35:28 +08:00
|
|
|
$set: {
|
2017-10-12 08:52:57 +08:00
|
|
|
whiteboardId,
|
2016-11-19 01:35:28 +08:00
|
|
|
meetingId,
|
2017-10-12 08:52:57 +08:00
|
|
|
id,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
};
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
// returning one document ready for upsert
|
2018-04-24 21:59:13 +08:00
|
|
|
return {
|
|
|
|
updateOne: {
|
|
|
|
'filter': selector,
|
|
|
|
'update': modifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
};
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleTextUpdate(meetingId, whiteboardId, userId, annotation) {
|
2018-01-08 08:25:56 +08:00
|
|
|
const {
|
|
|
|
id, status, annotationType, annotationInfo, wbId, position,
|
|
|
|
} = annotation;
|
2017-10-12 08:52:57 +08:00
|
|
|
|
|
|
|
const selector = {
|
|
|
|
meetingId,
|
|
|
|
id,
|
|
|
|
userId,
|
|
|
|
};
|
|
|
|
|
|
|
|
annotationInfo.text = annotationInfo.text.replace(/[\r]/g, '\n');
|
|
|
|
|
|
|
|
const modifier = {
|
|
|
|
$set: {
|
2016-11-19 01:35:28 +08:00
|
|
|
whiteboardId,
|
2017-10-12 08:52:57 +08:00
|
|
|
meetingId,
|
|
|
|
id,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position,
|
2016-11-19 01:35:28 +08:00
|
|
|
},
|
2017-10-12 08:52:57 +08:00
|
|
|
$inc: { version: 1 },
|
2016-11-19 01:35:28 +08:00
|
|
|
};
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
// returning one document ready for upsert
|
2018-04-25 02:17:55 +08:00
|
|
|
return {
|
|
|
|
updateOne: {
|
|
|
|
'filter': selector,
|
|
|
|
'update': modifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
};
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function handlePencilUpdate(meetingId, whiteboardId, userId, annotation) {
|
|
|
|
// fetching annotation statuses from the config
|
|
|
|
const ANOTATION_STATUSES = Meteor.settings.public.whiteboard.annotations.status;
|
|
|
|
const DRAW_START = ANOTATION_STATUSES.start;
|
|
|
|
const DRAW_UPDATE = ANOTATION_STATUSES.update;
|
|
|
|
const DRAW_END = ANOTATION_STATUSES.end;
|
|
|
|
|
2018-01-08 08:24:05 +08:00
|
|
|
const SERVER_CONFIG = Meteor.settings.private.app;
|
2017-10-12 08:52:57 +08:00
|
|
|
const PENCIL_CHUNK_SIZE = SERVER_CONFIG.pencilChunkLength || 100;
|
|
|
|
|
2018-01-08 08:25:56 +08:00
|
|
|
const {
|
|
|
|
id, status, annotationType, annotationInfo, wbId, position,
|
|
|
|
} = annotation;
|
2017-10-12 08:52:57 +08:00
|
|
|
|
|
|
|
const baseSelector = {
|
|
|
|
meetingId,
|
|
|
|
id,
|
|
|
|
userId,
|
2018-01-23 07:07:29 +08:00
|
|
|
whiteboardId,
|
2017-10-12 08:52:57 +08:00
|
|
|
};
|
|
|
|
let baseModifier;
|
|
|
|
let chunkSelector;
|
|
|
|
let chunkModifier;
|
|
|
|
|
|
|
|
// fetching the Annotation object
|
2018-04-25 23:15:36 +08:00
|
|
|
// if there's an update waiting inside a bulk, then use that info
|
|
|
|
// if not, then access Mongo
|
2018-04-24 21:59:13 +08:00
|
|
|
const bulkAnnotation = RedisPubSub.findAnnotationInsideBulk(baseSelector);
|
2018-04-25 23:15:36 +08:00
|
|
|
let Annotation = bulkAnnotation ? bulkAnnotation : Annotations.findOne(baseSelector);
|
2017-10-12 08:52:57 +08:00
|
|
|
|
|
|
|
// a helper func, to split the initial annotation.points into subdocuments
|
|
|
|
// returns an array of { selector, modifier } objects for subdocuments.
|
|
|
|
const createPencilObjects = () => {
|
|
|
|
const chunks = [];
|
|
|
|
// if the length of the points < PENCIL_CHUNK_SIZE then we simply return an array with one chunk
|
|
|
|
if (annotationInfo.points.length < PENCIL_CHUNK_SIZE) {
|
|
|
|
const chunkId = `${id}--${1}`;
|
|
|
|
chunks.push({
|
|
|
|
selector: {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: chunkId,
|
|
|
|
},
|
|
|
|
modifier: {
|
|
|
|
$set: {
|
|
|
|
whiteboardId,
|
|
|
|
meetingId,
|
|
|
|
id: chunkId,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
},
|
2016-11-19 01:35:28 +08:00
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
return chunks;
|
|
|
|
}
|
2016-11-19 01:35:28 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// *default flow*
|
|
|
|
// length of the points >= PENCIL_CHUNK_SIZE, so we split them into subdocuments
|
2016-11-19 01:35:28 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// counter is used for generating ids.
|
|
|
|
let i = 0;
|
|
|
|
let counter = 1;
|
|
|
|
for (; i <= annotationInfo.points.length; i += PENCIL_CHUNK_SIZE, counter += 1) {
|
|
|
|
const chunkId = `${id}--${counter}`;
|
|
|
|
|
|
|
|
// we always need to attach the last coordinate from the previous subdocument
|
|
|
|
// to the front of the current subdocument, to connect the pencil path
|
|
|
|
const _annotationInfo = annotationInfo;
|
|
|
|
_annotationInfo.points = annotationInfo.points.slice(i === 0 ? 0 : i - 2, PENCIL_CHUNK_SIZE);
|
|
|
|
|
|
|
|
chunks.push({
|
|
|
|
selector: {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: chunkId,
|
|
|
|
},
|
|
|
|
modifier: {
|
|
|
|
$set: {
|
|
|
|
whiteboardId,
|
|
|
|
meetingId,
|
|
|
|
id: chunkId,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo: _annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
},
|
2016-11-19 01:35:28 +08:00
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return chunks;
|
|
|
|
};
|
|
|
|
|
2018-04-24 21:59:13 +08:00
|
|
|
let operations = [];
|
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
switch (status) {
|
|
|
|
case DRAW_START: {
|
2018-04-24 21:59:13 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// on start we split the points
|
|
|
|
const chunks = createPencilObjects();
|
|
|
|
|
|
|
|
// create the 'pencil_base'
|
|
|
|
baseModifier = {
|
|
|
|
id,
|
|
|
|
userId,
|
|
|
|
meetingId,
|
2018-01-23 07:07:29 +08:00
|
|
|
whiteboardId,
|
2017-10-12 08:52:57 +08:00
|
|
|
position,
|
|
|
|
annotationType: 'pencil_base',
|
|
|
|
numberOfChunks: chunks.length,
|
2018-04-24 21:59:13 +08:00
|
|
|
lastChunkLength: chunks[chunks.length - 1].length ? chunks[chunks.length - 1].length : null,
|
2017-10-12 08:52:57 +08:00
|
|
|
lastCoordinate: [
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 2],
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 1],
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
// pushing all the chunks to the array of operations to add to the bulk later
|
2017-10-12 08:52:57 +08:00
|
|
|
for (let i = 0; i < chunks.length; i += 1) {
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': chunks[i].selector,
|
|
|
|
'update': chunks[i].modifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
2018-04-25 23:15:36 +08:00
|
|
|
// pencil_base upsert to be added to the bulk
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': baseSelector,
|
|
|
|
'update': baseModifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
|
2018-04-24 21:59:13 +08:00
|
|
|
return operations;
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
case DRAW_UPDATE: {
|
2018-04-24 21:59:13 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// checking if "pencil_base" exists
|
|
|
|
if (Annotation) {
|
2018-04-24 21:59:13 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
const { numberOfChunks, lastChunkLength } = Annotation;
|
|
|
|
|
|
|
|
// if lastChunkLength < PENCIL_CHUNK_SIZE then we can simply push points to the last object
|
|
|
|
if (lastChunkLength < PENCIL_CHUNK_SIZE) {
|
|
|
|
// creating a modifier for 'pencil_base'
|
|
|
|
baseModifier = {
|
|
|
|
$set: {
|
|
|
|
lastChunkLength: lastChunkLength + annotation.annotationInfo.points.length,
|
|
|
|
lastCoordinate: [
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 2],
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 1],
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const chunkId = `${id}--${numberOfChunks}`;
|
|
|
|
chunkSelector = {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: chunkId,
|
|
|
|
};
|
|
|
|
|
|
|
|
// fetching the last pencil sub-document
|
2018-04-25 23:15:36 +08:00
|
|
|
// if there's a proper chunk update in the bulk, use its info
|
|
|
|
// if not, access Mongo
|
2018-04-24 21:59:13 +08:00
|
|
|
const bulkChunk = RedisPubSub.findChunkInsideBulk(chunkSelector);
|
|
|
|
let chunk = (bulkChunk && bulkChunk.$set) ? bulkChunk.$set : Annotations.findOne(chunkSelector);
|
2018-04-25 23:15:36 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// adding the coordinates to the end of the last sub-document
|
|
|
|
annotationInfo.points = chunk.annotationInfo.points.concat(annotationInfo.points);
|
|
|
|
|
|
|
|
chunkModifier = {
|
|
|
|
$set: {
|
|
|
|
annotationInfo,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
// if lastChunkLength > PENCIL_CHUNK_SIZE then we need to create another chunk
|
|
|
|
} else if (lastChunkLength >= PENCIL_CHUNK_SIZE) {
|
|
|
|
baseModifier = {
|
|
|
|
$set: {
|
|
|
|
numberOfChunks: numberOfChunks + 1,
|
|
|
|
lastChunkLength: annotationInfo.points.length,
|
|
|
|
lastCoordinate: [
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 2],
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 1],
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const chunkId = `${id}--${numberOfChunks + 1}`;
|
|
|
|
chunkSelector = {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: chunkId,
|
|
|
|
};
|
|
|
|
|
|
|
|
// pushing the last coordinate to the front of the current chunk's points
|
|
|
|
annotationInfo.points.unshift(Annotation.lastCoordinate[0], Annotation.lastCoordinate[1]);
|
|
|
|
|
|
|
|
chunkModifier = {
|
|
|
|
$set: {
|
|
|
|
whiteboardId,
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: chunkId,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position: Annotation.position,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
// chunk upsert to be added to the bulk later
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': chunkSelector,
|
|
|
|
'update': chunkModifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
2018-04-25 23:15:36 +08:00
|
|
|
// pencil_base upsert to be added to the bulk later
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': baseSelector,
|
|
|
|
'update': baseModifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return operations;
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// **default flow**
|
|
|
|
// if we are here then it means that Annotation object is not in the db
|
|
|
|
// So creating everything similar to DRAW_START case
|
|
|
|
const _chunks = createPencilObjects();
|
|
|
|
|
|
|
|
// creating 'pencil_base' based on the info we received from createPencilObjects()
|
|
|
|
baseModifier = {
|
|
|
|
id,
|
|
|
|
userId,
|
|
|
|
meetingId,
|
2018-01-23 07:07:29 +08:00
|
|
|
whiteboardId,
|
2017-10-12 08:52:57 +08:00
|
|
|
position,
|
|
|
|
annotationType: 'pencil_base',
|
|
|
|
numberOfChunks: _chunks.length,
|
|
|
|
lastChunkLength: _chunks[_chunks.length - 1].length,
|
|
|
|
lastCoordinate: [
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 2],
|
|
|
|
annotationInfo.points[annotationInfo.points.length - 1],
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
// adding all the chunk upserts to the array of operations
|
2017-10-12 08:52:57 +08:00
|
|
|
for (let i = 0; i < _chunks.length; i += 1) {
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': _chunks[i].selector,
|
|
|
|
'update': _chunks[i].modifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
2018-04-25 23:15:36 +08:00
|
|
|
// pencil_base upsert
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': baseSelector,
|
|
|
|
'update': baseModifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return operations;
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
case DRAW_END: {
|
2018-04-24 21:59:13 +08:00
|
|
|
|
2017-10-12 08:52:57 +08:00
|
|
|
// If a user just finished drawing with the pencil
|
|
|
|
// Removing all the sub-documents and replacing the 'pencil_base'
|
|
|
|
if (Annotation && Annotation.annotationType === 'pencil_base') {
|
|
|
|
// delete everything and replace base
|
|
|
|
const chunkIds = [];
|
2018-04-25 22:52:03 +08:00
|
|
|
for (let i = 0; i <= Annotation.numberOfChunks; i += 1) {
|
2017-10-12 08:52:57 +08:00
|
|
|
chunkIds.push(`${Annotation.id}--${i}`);
|
|
|
|
}
|
|
|
|
chunkSelector = {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
id: { $in: chunkIds },
|
|
|
|
};
|
|
|
|
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
deleteMany: {
|
|
|
|
'filter': chunkSelector
|
|
|
|
}
|
|
|
|
});
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Updating the main pencil object with the final info
|
|
|
|
baseModifier = {
|
|
|
|
$set: {
|
|
|
|
whiteboardId,
|
|
|
|
meetingId,
|
|
|
|
id,
|
|
|
|
status,
|
|
|
|
annotationType,
|
|
|
|
annotationInfo,
|
|
|
|
wbId,
|
|
|
|
position,
|
|
|
|
},
|
|
|
|
$inc: { version: 1 },
|
|
|
|
$unset: {
|
|
|
|
numberOfChunks: '',
|
|
|
|
lastChunkLength: '',
|
|
|
|
lastCoordinate: '',
|
|
|
|
},
|
|
|
|
};
|
2018-04-24 21:59:13 +08:00
|
|
|
operations.push({
|
|
|
|
updateOne: {
|
|
|
|
'filter': baseSelector,
|
|
|
|
'update': baseModifier,
|
|
|
|
'upsert': true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return operations;
|
2017-10-12 08:52:57 +08:00
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function addAnnotation(meetingId, whiteboardId, userId, annotation) {
|
|
|
|
check(meetingId, String);
|
|
|
|
check(whiteboardId, String);
|
|
|
|
check(annotation, Object);
|
|
|
|
|
|
|
|
let query;
|
|
|
|
|
|
|
|
switch (annotation.annotationType) {
|
|
|
|
case ANNOTATION_TYPE_TEXT:
|
|
|
|
query = handleTextUpdate(meetingId, whiteboardId, userId, annotation);
|
|
|
|
break;
|
|
|
|
case ANNOTATION_TYPE_PENCIL:
|
|
|
|
query = handlePencilUpdate(meetingId, whiteboardId, userId, annotation);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
query = handleCommonAnnotation(meetingId, whiteboardId, userId, annotation);
|
2016-11-19 01:35:28 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-04-25 23:15:36 +08:00
|
|
|
if(query instanceof Array) { // array of operations
|
2018-04-24 21:59:13 +08:00
|
|
|
RedisPubSub.addOperationsToBulk(query);
|
2018-04-25 23:15:36 +08:00
|
|
|
} else { // one operation
|
2018-04-24 21:59:13 +08:00
|
|
|
RedisPubSub.addToAnnotationsBulk(query);
|
2018-04-06 04:19:41 +08:00
|
|
|
}
|
2018-04-24 21:59:13 +08:00
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
}
|
2018-04-24 21:59:13 +08:00
|
|
|
|