Merge pull request #4154 from Klauswk/slides-refactor-2x

[HTML5 2.0] Refactor Code to reflect AkkaApps collections
This commit is contained in:
Anton Georgiev 2017-08-02 18:06:49 -04:00 committed by GitHub
commit 9252371948
81 changed files with 722 additions and 635 deletions

View File

@ -1,8 +1,3 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import switchSlide from './methods/switchSlide';
Meteor.methods(mapToAcl(['methods.switchSlide', 'methods.switchSlideMessage'], {
switchSlide,
switchSlideMessage: switchSlide, // legacy
}));
Meteor.methods();

View File

@ -0,0 +1 @@
export default new Mongo.Collection('annotations');

View File

@ -2,7 +2,9 @@ import RedisPubSub from '/imports/startup/server/redis2x';
import handleWhiteboardCleared from './handlers/whiteboardCleared';
import handleWhiteboardUndo from './handlers/whiteboardUndo';
import handleWhiteboardSend from './handlers/whiteboardSend';
import handleWhiteboardAnnotations from './handlers/whiteboardAnnotations';
RedisPubSub.on('ClearWhiteboardEvtMsg', handleWhiteboardCleared);
RedisPubSub.on('UndoWhiteboardEvtMsg', handleWhiteboardUndo);
RedisPubSub.on('SendWhiteboardAnnotationEvtMsg', handleWhiteboardSend);
RedisPubSub.on('GetWhiteboardAnnotationsRespMsg', handleWhiteboardAnnotations);

View File

@ -0,0 +1,30 @@
import _ from 'lodash';
import { check } from 'meteor/check';
import Annotations from '/imports/api/2.0/annotations';
import addAnnotation from '../modifiers/addAnnotation';
import removeAnnotation from '../modifiers/removeAnnotation';
export default function handleWhiteboardAnnotations({ body }, meetingId) {
check(meetingId, String);
check(body, Object);
const { annotations, whiteboardId } = body;
const annotationIds = annotations.map(a => a.id);
const annotationsToRemove = Annotations.find({
meetingId,
wbId: whiteboardId,
'annotationInfo.id': { $nin: annotationIds },
}).fetch();
_.each(annotationsToRemove, (annotation) => {
removeAnnotation(meetingId, annotation.whiteboardId, annotation.annotationInfo.id);
});
const annotationsAdded = [];
_.each(annotations, (annotation) => {
const { wbId, userId } = annotation;
annotationsAdded.push(addAnnotation(meetingId, wbId, userId, annotation));
});
return annotationsAdded;
}

View File

@ -1,11 +1,11 @@
import { check } from 'meteor/check';
import clearShapesWhiteboard from '../modifiers/clearShapesWhiteboard';
import clearAnnotations from '../modifiers/clearAnnotations';
export default function handleWhiteboardCleared({ body }, meetingId) {
const whiteboardId = body.whiteboardId;
check(whiteboardId, String);
return clearShapesWhiteboard(meetingId, whiteboardId);
return clearAnnotations(meetingId, whiteboardId);
}

View File

@ -0,0 +1,17 @@
import { check } from 'meteor/check';
import addAnnotation from '../modifiers/addAnnotation';
export default function handleWhiteboardSend({ header, body }, meetingId) {
const userId = header.userId;
const annotation = body.annotation;
check(userId, String);
check(annotation, Object);
const whiteboardId = annotation.wbId;
check(whiteboardId, String);
return addAnnotation(meetingId, whiteboardId, userId, annotation);
}

View File

@ -1,6 +1,6 @@
import { check } from 'meteor/check';
import removeShape from '../modifiers/removeShape';
import removeAnnotation from '../modifiers/removeAnnotation';
export default function handleWhiteboardUndo({ body }, meetingId) {
const whiteboardId = body.whiteboardId;
@ -9,5 +9,5 @@ export default function handleWhiteboardUndo({ body }, meetingId) {
check(whiteboardId, String);
check(shapeId, String);
return removeShape(meetingId, whiteboardId, shapeId);
return removeAnnotation(meetingId, whiteboardId, shapeId);
}

View File

@ -0,0 +1,4 @@
import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
Meteor.methods();

View File

@ -0,0 +1,101 @@
import { Match, check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Annotations from '/imports/api/2.0/annotations';
const ANNOTATION_TYPE_TEXT = 'text';
const ANNOTATION_TYPE_PENCIL = 'pencil';
export default function addAnnotation(meetingId, whiteboardId, userId, annotation) {
check(meetingId, String);
check(whiteboardId, String);
check(annotation, {
id: String,
status: String,
annotationType: String,
annotationInfo: {
x: Match.Maybe(Number), // Text Annotation Only.
y: Match.Maybe(Number), // Text Annotation Only.
text: Match.Maybe(String), // Text Annotation Only.
fontColor: Match.Maybe(Number), // Text Annotation Only.
calcedFontSize: Match.Maybe(Number), // Text Annotation Only.
textBoxWidth: Match.Maybe(Number), // Text Annotation Only.
textBoxHeight: Match.Maybe(Number), // Text Annotation Only.
fontSize: Match.Maybe(Number), // Text Annotation Only.
dataPoints: Match.Maybe(String), // Text Annotation Only.
color: Match.Maybe(Number), // Draw Annotation Only.
thickness: Match.Maybe(Number), // Draw Annotation Only.
transparency: Match.Maybe(Boolean), // Draw Annotation Only.
points: Match.Maybe([Number]), // Draw and Poll Annotation Only.
numResponders: Match.Maybe(Number), // Poll Only Annotation.
result: Match.Maybe([{
id: Number,
key: String,
numVotes: Number,
}]), // Poll Only Annotation.
numRespondents: Match.Maybe(Number), // Poll Only Annotation.
id: String,
whiteboardId: String,
status: String,
type: String,
commands: Match.Maybe([Number]),
},
wbId: String,
userId: String,
position: Number,
});
const { id, status, annotationType, annotationInfo, wbId, position } = annotation;
const selector = {
meetingId,
id: annotation.id,
userId,
};
const modifier = {
$set: {
whiteboardId,
meetingId,
id,
status,
annotationType,
annotationInfo,
wbId,
position,
},
};
const shapeType = annotation.annotationType;
switch (shapeType) {
case ANNOTATION_TYPE_TEXT:
modifier.$set.annotationInfo.text = annotation.annotationInfo.text.replace(/[\r]/g, '\n');
break;
case ANNOTATION_TYPE_PENCIL:
// On the draw_end he send us all the points, we don't need to push, we can simple
// set the new points.
if (annotation.status !== 'DRAW_END') {
// We don't want it to be update twice.
delete modifier.$set.annotationInfo;
modifier.$push = { 'annotationInfo.points': { $each: annotation.annotationInfo.points } };
}
break;
default:
break;
}
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Adding annotation2x to collection: ${err}`);
}
const { insertedId } = numChanged;
if (insertedId) {
return Logger.info(`Added annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
}
return Logger.info(`Upserted annotation2x id=${annotation.id} whiteboard=${whiteboardId}`);
};
return Annotations.upsert(selector, modifier, cb);
}

View File

@ -0,0 +1,12 @@
import Annotations from '/imports/api/2.0/annotations';
import Logger from '/imports/startup/server/logger';
export default function clearAnnotations(meetingId, whiteboardId) {
if (meetingId && whiteboardId) {
return Annotations.remove({ meetingId, whiteboardId }, Logger.info(`Cleared Annotations from whiteboard ${whiteboardId} (${meetingId})`));
} else if (meetingId) {
return Annotations.remove({ meetingId }, Logger.info(`Cleared Annotations (${meetingId})`));
}
return Annotations.remove({}, Logger.info('Cleared Annotations (all)'));
}

View File

@ -0,0 +1,25 @@
import { check } from 'meteor/check';
import Annotations from '/imports/api/2.0/annotations';
import Logger from '/imports/startup/server/logger';
export default function removeAnnotation(meetingId, whiteboardId, shapeId) {
check(meetingId, String);
check(whiteboardId, String);
check(shapeId, String);
const selector = {
meetingId,
whiteboardId,
id: shapeId,
};
const cb = (err) => {
if (err) {
return Logger.error(`Removing annotation from collection: ${err}`);
}
return Logger.info(`Removed annotation id=${shapeId} whiteboard=${whiteboardId}`);
};
return Annotations.remove(selector, cb);
}

View File

@ -0,0 +1,24 @@
import Annotations from '/imports/api/2.0/annotations';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function annotations(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(requesterToken, String);
Logger.info(`Publishing Annotations2x for ${meetingId} ${requesterUserId} ${requesterToken}`);
return Annotations.find({ meetingId });
}
function publish(...args) {
const boundAnnotations = annotations.bind(this);
return mapToAcl('subscriptions.annotations', boundAnnotations)(args);
}
Meteor.publish('annotations', publish);

View File

@ -11,7 +11,7 @@ class BBB {
}
getUsername() {
return Users.findOne({ userId: this.getUserId() }).user.name;
return Users.findOne({ userId: this.getUserId() }).name;
}
getExtension() {

View File

@ -1,6 +1,7 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Breakouts from '/imports/api/2.0/breakouts';
import flat from 'flat';
export default function addBreakout(breakout) {
const {
@ -16,11 +17,8 @@ export default function addBreakout(breakout) {
const selector = { breakoutMeetingId };
const modifier = {
$set: {
breakoutMeetingId,
parentId,
name,
},
$set:
flat(breakout, { safe: true }),
};
const cb = (err, numChanged) => {

View File

@ -1,6 +1,4 @@
import Logger from '/imports/startup/server/logger';
// FIX ME, ADD THIS METHOD TO THE MEETING 2.0
import removeMeeting from '/imports/api/1.1/meetings/server/modifiers/removeMeeting';
import Breakouts from '/imports/api/2.0/breakouts';
export default function clearBreakouts(breakoutMeetingId) {
@ -9,12 +7,7 @@ export default function clearBreakouts(breakoutMeetingId) {
breakoutMeetingId,
};
const cb = () => {
Logger.info(`Cleared Breakouts (${breakoutMeetingId})`);
removeMeeting(breakoutMeetingId);
};
return Breakouts.remove(selector, cb);
return Breakouts.remove(selector);
}
return Breakouts.remove({}, Logger.info('Cleared Breakouts (all)'));

View File

@ -1,17 +1,26 @@
import { check } from 'meteor/check';
import { Match, check } from 'meteor/check';
import Captions from '/imports/api/2.0/captions';
import Logger from '/imports/startup/server/logger';
export default function addCaption(meetingId, locale, captionHistory, id = false) {
check(meetingId, String);
check(locale, String);
check(captionHistory, Object);
check(captionHistory, {
ownerId: String,
index: Number,
captions: String,
locale: Match.Maybe(String),
localeCode: Match.Maybe(String),
next: Match.OneOf(Number, undefined, null),
});
const selector = {
meetingId,
locale,
};
if (id) {
selector._id = id;
} else {
@ -22,11 +31,7 @@ export default function addCaption(meetingId, locale, captionHistory, id = false
$set: {
meetingId,
locale,
'captionHistory.locale': locale,
'captionHistory.ownerId': captionHistory.ownerId,
'captionHistory.captions': captionHistory.captions,
'captionHistory.next': captionHistory.next,
'captionHistory.index': captionHistory.index,
captionHistory,
},
};

View File

@ -1,7 +1,7 @@
import flat from 'flat';
import Chat from '/imports/api/2.0/chat';
import Logger from '/imports/startup/server/logger';
import { check } from 'meteor/check';
import { Match, check } from 'meteor/check';
import { BREAK_LINE } from '/imports/utils/lineEndings';
const parseMessage = (message) => {
@ -18,6 +18,17 @@ const parseMessage = (message) => {
};
export default function addChat(meetingId, message) {
check(message, {
message: String,
fromColor: String,
toUserId: String,
toUsername: String,
fromUserId: String,
fromUsername: Match.Maybe(String),
fromTime: Number,
fromTimezoneOffset: Match.Maybe(Number),
});
const parsedMessage = message;
parsedMessage.message = parseMessage(message.message);
@ -27,17 +38,15 @@ export default function addChat(meetingId, message) {
check(fromUserId, String);
check(toUserId, String);
const selector = {
meetingId,
'message.fromTime': parsedMessage.fromTime,
'message.fromUserId': parsedMessage.fromUserId,
'message.toUserId': parsedMessage.toUserId,
};
const selector = Object.assign(
{ meetingId },
flat(message),
);
const modifier = {
$set: {
meetingId,
message: flat(parsedMessage),
message: flat(parsedMessage, { safe: true }),
},
};

View File

@ -7,8 +7,59 @@ import initializeCursor from '/imports/api/1.1/cursor/server/modifiers/initializ
export default function addMeeting(meeting) {
const meetingId = meeting.meetingProp.intId;
check(meeting, Object);
check(meetingId, String);
check(meeting, {
breakoutProps: {
sequence: Number,
breakoutRooms: Array,
parentId: String,
},
meetingProp: {
intId: String,
extId: String,
isBreakout: Boolean,
name: String,
},
usersProp: {
webcamsOnlyForModerator: Boolean,
guestPolicy: String,
maxUsers: Number,
},
durationProps: {
createdTime: Number,
duration: Number,
createdDate: String,
maxInactivityTimeoutMinutes: Number,
warnMinutesBeforeMax: Number,
meetingExpireIfNoUserJoinedInMinutes: Number,
meetingExpireWhenLastUserLeftInMinutes: Number,
},
welcomeProp: {
welcomeMsg: String,
modOnlyMessage: String,
welcomeMsgTemplate: String,
},
recordProp: {
allowStartStopRecording: Boolean,
autoStartRecording: Boolean,
record: Boolean,
},
password: {
viewerPass: String,
moderatorPass: String,
},
voiceProp: {
voiceConf: String,
dialNumber: String,
telVoice: String,
},
screenshareProps: {
red5ScreenshareIp: String,
red5ScreenshareApp: String,
screenshareConf: String,
},
metadataProp: Object,
});
const selector = {
meetingId,
@ -17,7 +68,7 @@ export default function addMeeting(meeting) {
const modifier = {
$set: Object.assign(
{ meetingId },
flat(meeting),
flat(meeting, { safe: true }),
),
};

View File

@ -4,7 +4,7 @@ import Logger from '/imports/startup/server/logger';
import clearUsers from '/imports/api/2.0/users/server/modifiers/clearUsers';
import clearChats from '/imports/api/2.0/chat/server/modifiers/clearChats';
import clearBreakouts from '/imports/api/2.0/breakouts/server/modifiers/clearBreakouts';
import clearShapes from '/imports/api/2.0/shapes/server/modifiers/clearShapes';
import clearAnnotations from '/imports/api/2.0/annotations/server/modifiers/clearAnnotations';
import clearSlides from '/imports/api/2.0/slides/server/modifiers/clearSlides';
import clearPolls from '/imports/api/2.0/polls/server/modifiers/clearPolls';
import clearCursor from '/imports/api/2.0/cursor/server/modifiers/clearCursor';
@ -20,7 +20,7 @@ export default function removeMeeting(meetingId) {
clearPresentations(meetingId);
clearBreakouts(meetingId);
clearPolls(meetingId);
clearShapes(meetingId);
clearAnnotations(meetingId);
clearSlides(meetingId);
clearUsers(meetingId);
clearVoiceUsers(meetingId);

View File

@ -3,7 +3,7 @@ import { check } from 'meteor/check';
import Polls from '/imports/api/2.0/polls';
import Logger from '/imports/startup/server/logger';
export default function publishVote(credentials, pollId, pollAnswerId) { // TODO discuss location
export default function publishVote(credentials, id, pollAnswerId) { // TODO discuss location
const REDIS_CONFIG = Meteor.settings.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'RespondToPollReqMsg';
@ -13,8 +13,8 @@ export default function publishVote(credentials, pollId, pollAnswerId) { // TODO
const currentPoll = Polls.findOne({
users: requesterUserId,
meetingId,
'poll.answers.id': pollAnswerId,
'poll.id': pollId,
'answers.id': pollAnswerId,
id,
});
check(meetingId, String);
@ -24,7 +24,7 @@ export default function publishVote(credentials, pollId, pollAnswerId) { // TODO
const payload = {
requesterId: requesterUserId,
pollId: currentPoll.poll.id,
pollId: currentPoll.id,
questionId: 0,
answerId: pollAnswerId,
};
@ -38,7 +38,7 @@ export default function publishVote(credentials, pollId, pollAnswerId) { // TODO
const selector = {
users: requesterUserId,
meetingId,
'poll.answers.id': pollAnswerId,
'answers.id': pollAnswerId,
};
const modifier = {
@ -53,7 +53,7 @@ export default function publishVote(credentials, pollId, pollAnswerId) { // TODO
}
return Logger.info(`Updating Polls2x collection (meetingId: ${meetingId},
pollId: ${currentPoll.poll.id}!)`);
pollId: ${currentPoll.id}!)`);
};
Polls.update(selector, modifier, cb);

View File

@ -1,40 +1,50 @@
import Users from '/imports/api/2.0/users';
import Polls from '/imports/api/2.0/polls';
import Logger from '/imports/startup/server/logger';
import flat from 'flat';
import { check } from 'meteor/check';
export default function addPoll(meetingId, requesterId, poll) {
check(poll, Object);
check(requesterId, String);
check(meetingId, String);
check(poll, {
id: String,
answers: [
{
id: Number,
key: String,
},
],
});
let selector = {
meetingId,
};
const options = {
fields: {
'user.userid': 1,
userId: 1,
_id: 0,
},
};
const userIds = Users.find(selector, options)
.fetch()
.map(user => user.user.userid);
.fetch()
.map(user => user.userId);
selector = {
meetingId,
requester: requesterId,
'poll.id': poll.id,
id: poll.id,
};
const modifier = {
meetingId,
poll,
requester: requesterId,
users: userIds,
};
const modifier = Object.assign(
{ meetingId },
{ requester: requesterId },
{ users: userIds },
flat(poll, { safe: true }),
);
const cb = (err, numChanged) => {
if (err != null) {

View File

@ -2,13 +2,13 @@ import Polls from '/imports/api/2.0/polls';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
export default function removePoll(meetingId, pollId) {
export default function removePoll(meetingId, id) {
check(meetingId, String);
check(pollId, String);
check(id, String);
const selector = {
meetingId,
'poll.id': pollId,
id,
};
const cb = (err) => {
@ -16,7 +16,7 @@ export default function removePoll(meetingId, pollId) {
return Logger.error(`Removing Poll2x from collection: ${err}`);
}
return Logger.info(`Removed Poll2x id=${pollId}`);
return Logger.info(`Removed Poll2x id=${id}`);
};
return Polls.remove(selector, cb);

View File

@ -1,6 +1,7 @@
import Polls from '/imports/api/2.0/polls';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import flat from 'flat';
export default function updateVotes(poll, meetingId, requesterId) {
check(meetingId, String);
@ -24,19 +25,14 @@ export default function updateVotes(poll, meetingId, requesterId) {
const selector = {
meetingId,
requester: requesterId,
'poll.id': id,
id,
};
const modifier = {
$set: {
requester: requesterId,
poll: {
answers,
num_responders: numResponders,
num_respondents: numRespondents,
id,
},
},
$set: Object.assign(
{ requester: requesterId },
flat(poll, { safe: true }),
),
};
const cb = (err) => {

View File

@ -8,11 +8,11 @@ const clearCurrentPresentation = (meetingId, presentationId) => {
const selector = {
meetingId,
presentationId: { $ne: presentationId },
'presentation.current': true,
current: true,
};
const modifier = {
$set: { 'presentation.current': false },
$set: { current: false },
};
const cb = (err, numChanged) => {

View File

@ -13,10 +13,10 @@ export default function handlePresentationInfoReply({ body }, meetingId) {
const presentationsToRemove = Presentations.find({
meetingId,
'presentation.id': { $nin: presentationsIds },
id: { $nin: presentationsIds },
}).fetch();
presentationsToRemove.forEach(p => removePresentation(meetingId, p.presentation.id));
presentationsToRemove.forEach(p => removePresentation(meetingId, p.id));
const presentationsAdded = [];
presentations.forEach((presentation) => {

View File

@ -1,6 +1,7 @@
import { check } from 'meteor/check';
import Presentations from '/imports/api/2.0/presentations';
import Logger from '/imports/startup/server/logger';
import flat from 'flat';
import addSlide from '/imports/api/2.0/slides/server/modifiers/addSlide';
@ -15,21 +16,36 @@ const addSlides = (meetingId, presentationId, slides) => {
};
export default function addPresentation(meetingId, presentation) {
check(presentation, Object);
check(presentation, {
id: String,
name: String,
current: Boolean,
pages: [{
id: String,
num: Number,
thumbUri: String,
swfUri: String,
txtUri: String,
svgUri: String,
current: Boolean,
xOffset: Number,
yOffset: Number,
widthRatio: Number,
heightRatio: Number,
}],
downloadable: Boolean,
});
const selector = {
meetingId,
'presentation.id': presentation.id,
id: presentation.id,
};
const modifier = {
$set: {
meetingId,
'presentation.id': presentation.id,
'presentation.name': presentation.name,
'presentation.current': presentation.current,
'presentation.downloadable': presentation.downloadable,
},
$set: Object.assign(
{ meetingId },
flat(presentation, { safe: true }),
),
};
const cb = (err, numChanged) => {

View File

@ -9,10 +9,10 @@ export default function changeCurrentPresentation(meetingId, presentationId) {
const oldCurrent = {
selector: {
meetingId,
'presentation.current': true,
current: true,
},
modifier: {
$set: { 'presentation.current': false },
$set: { current: false },
},
callback: (err) => {
if (err) {
@ -26,10 +26,10 @@ export default function changeCurrentPresentation(meetingId, presentationId) {
const newCurrent = {
selector: {
meetingId,
'presentation.id': presentationId,
id: presentationId,
},
modifier: {
$set: { 'presentation.current': true },
$set: { current: true },
},
callback: (err) => {
if (err) {

View File

@ -10,7 +10,7 @@ export default function removePresentation(meetingId, presentationId) {
const selector = {
meetingId,
'presentation.id': presentationId,
id: presentationId,
};
const cb = (err, numChanged) => {

View File

@ -1 +0,0 @@
export default new Mongo.Collection('shapes2x');

View File

@ -1,17 +0,0 @@
import { check } from 'meteor/check';
import addShape from '../modifiers/addShape';
export default function handleWhiteboardSend({ header, body }, meetingId) {
const userId = header.userId;
const shape = body.annotation;
check(userId, String);
check(shape, Object);
const whiteboardId = shape.wbId;
check(whiteboardId, String);
return addShape(meetingId, whiteboardId, userId, shape);
}

View File

@ -1,4 +0,0 @@
import { Meteor } from 'meteor/meteor';
Meteor.methods({
});

View File

@ -1,84 +0,0 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Shapes from '/imports/api/2.0/shapes';
const SHAPE_TYPE_TEXT = 'text';
const SHAPE_TYPE_PENCIL = 'pencil';
export default function addShape(meetingId, whiteboardId, userId, shape) {
check(meetingId, String);
check(whiteboardId, String);
check(shape, Object);
const selector = {
meetingId,
'shape.id': shape.id,
userId,
};
const modifier = {
$set: {
userId,
meetingId,
whiteboardId,
'shape.id': shape.id,
'shape.wb_id': shape.wbId,
'shape.shape_type': shape.annotationType,
'shape.status': shape.status,
'shape.shape.type': shape.annotationInfo.type,
'shape.shape.status': shape.annotationInfo.status,
},
};
const shapeType = shape.annotationType;
switch (shapeType) {
case SHAPE_TYPE_TEXT:
modifier.$set = Object.assign(modifier.$set, {
'shape.shape.x': shape.annotationInfo.x,
'shape.shape.y': shape.annotationInfo.y,
'shape.shape.fontColor': shape.annotationInfo.fontColor,
'shape.shape.calcedFontSize': shape.annotationInfo.calcedFontSize,
'shape.shape.textBoxWidth': shape.annotationInfo.textBoxWidth,
'shape.shape.text': shape.annotationInfo.text.replace(/[\r]/g, '\n'),
'shape.shape.textBoxHeight': shape.annotationInfo.textBoxHeight,
'shape.shape.id': shape.annotationInfo.id,
'shape.shape.whiteboardId': shape.annotationInfo.whiteboardId,
'shape.shape.fontSize': shape.annotationInfo.fontSize,
'shape.shape.dataPoints': shape.annotationInfo.dataPoints,
});
break;
case SHAPE_TYPE_PENCIL:
modifier.$push = { 'shape.shape.points': { $each: shape.annotationInfo.points } };
break;
default:
modifier.$set = Object.assign(modifier.$set, {
'shape.shape.points': shape.annotationInfo.points,
'shape.shape.whiteboardId': shape.annotationInfo.whiteboardId,
'shape.shape.id': shape.annotationInfo.id,
'shape.shape.square': shape.annotationInfo.square,
'shape.shape.transparency': shape.annotationInfo.transparency,
'shape.shape.thickness': shape.annotationInfo.thickness * 10,
'shape.shape.color': shape.annotationInfo.color,
'shape.shape.result': shape.annotationInfo.result,
'shape.shape.num_respondents': shape.annotationInfo.numRespondents,
'shape.shape.num_responders': shape.annotationInfo.numResponders,
});
break;
}
const cb = (err, numChanged) => {
if (err) {
return Logger.error(`Adding shape to collection: ${err}`);
}
const { insertedId } = numChanged;
if (insertedId) {
return Logger.info(`Added shape id=${shape.id} whiteboard=${whiteboardId}`);
}
return Logger.info(`Upserted shape id=${shape.id} whiteboard=${whiteboardId}`);
};
return Shapes.upsert(selector, modifier, cb);
}

View File

@ -1,10 +0,0 @@
import Shapes from '/imports/api/2.0/shapes';
import Logger from '/imports/startup/server/logger';
export default function clearShapes(meetingId) {
if (meetingId) {
return Shapes.remove({ meetingId }, Logger.info(`Cleared Shapes (${meetingId})`));
}
return Shapes.remove({}, Logger.info('Cleared Shapes (all)'));
}

View File

@ -1,23 +0,0 @@
import Shapes from '/imports/api/2.0/shapes';
import Logger from '/imports/startup/server/logger';
import { check } from 'meteor/check';
export default function clearShapesWhiteboard(meetingId, whiteboardId) {
check(meetingId, String);
check(whiteboardId, String);
const selector = {
meetingId,
whiteboardId,
};
const cb = (err) => {
if (err) {
return Logger.error(`Removing Shapes2x from collection: ${err}`);
}
return Logger.info(`Removed Shapes2x where whiteboard=${whiteboardId}`);
};
return Shapes.remove(selector, cb);
}

View File

@ -1,25 +0,0 @@
import { check } from 'meteor/check';
import Shapes from '/imports/api/2.0/shapes';
import Logger from '/imports/startup/server/logger';
export default function removeShape(meetingId, whiteboardId, shapeId) {
check(meetingId, String);
check(whiteboardId, String);
check(shapeId, String);
const selector = {
meetingId,
whiteboardId,
'shape.id': shapeId,
};
const cb = (err) => {
if (err) {
return Logger.error(`Removing shape2x from collection: ${err}`);
}
return Logger.info(`Removed shape2x id=${shapeId} whiteboard=${whiteboardId}`);
};
return Shapes.remove(selector, cb);
}

View File

@ -1,24 +0,0 @@
import Shapes from '/imports/api/2.0/shapes';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import mapToAcl from '/imports/startup/mapToAcl';
function shapes(credentials) {
const { meetingId, requesterUserId, requesterToken } = credentials;
check(meetingId, String);
check(requesterUserId, String);
check(requesterToken, String);
Logger.info(`Publishing Shapes2x for ${meetingId} ${requesterUserId} ${requesterToken}`);
return Shapes.find({ meetingId });
}
function publish(...args) {
const boundShapes = shapes.bind(this);
return mapToAcl('subscriptions.shapes', boundShapes)(args);
}
Meteor.publish('shapes2x', publish);

View File

@ -2,7 +2,6 @@ import { Meteor } from 'meteor/meteor';
import mapToAcl from '/imports/startup/mapToAcl';
import switchSlide from './methods/switchSlide';
Meteor.methods(mapToAcl(['methods.switchSlide', 'methods.switchSlideMessage'], {
// switchSlide,
// switchSlideMessage: switchSlide, // legacy
Meteor.methods(mapToAcl(['methods.switchSlide'], {
switchSlide,
}));

View File

@ -7,8 +7,8 @@ import RedisPubSub from '/imports/startup/server/redis2x';
export default function switchSlide(credentials, slideNumber) {
const REDIS_CONFIG = Meteor.settings.redis;
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.presentation;
const EVENT_NAME = 'go_to_slide';
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'SetCurrentPagePubMsg';
const { meetingId, requesterUserId, requesterToken } = credentials;
@ -17,10 +17,12 @@ export default function switchSlide(credentials, slideNumber) {
check(requesterToken, String);
check(slideNumber, Number);
const Presentation = Presentations.findOne({
const selector = {
meetingId,
'presentation.current': true,
});
current: true,
};
const Presentation = Presentations.findOne(selector);
if (!Presentation) {
throw new Meteor.Error(
@ -29,8 +31,8 @@ export default function switchSlide(credentials, slideNumber) {
const Slide = Slides.findOne({
meetingId,
presentationId: Presentation.presentation.id,
'slide.num': parseInt(slideNumber, 2),
presentationId: Presentation.id,
num: slideNumber,
});
if (!Slide) {
@ -38,10 +40,12 @@ export default function switchSlide(credentials, slideNumber) {
'slide-not-found', `Slide number ${slideNumber} not found in the current presentation`);
}
const header = { name: EVENT_NAME, meetingId, userId: requesterUserId };
const payload = {
page: Slide.slide.id,
meeting_id: meetingId,
pageId: Slide.id,
presentationId: Presentation.id,
};
return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
return RedisPubSub.publish(CHANNEL, EVENT_NAME, meetingId, payload, header);
}

View File

@ -1,6 +1,7 @@
import probe from 'probe-image-size';
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import flat from 'flat';
import RedisPubSub from '/imports/startup/server/redis2x';
import Slides from '/imports/api/2.0/slides';
import Logger from '/imports/startup/server/logger';
@ -8,17 +9,16 @@ import { SVG, PNG } from '/imports/utils/mimeTypes';
const requestWhiteboardHistory = (meetingId, slideId) => {
const REDIS_CONFIG = Meteor.settings.redis;
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.whiteboard;
const EVENT_NAME = 'request_whiteboard_annotation_history_request';
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'GetWhiteboardAnnotationsReqMsg';
const header = { name: EVENT_NAME, meetingId, userId: 'nodeJSapp' };
const payload = {
meeting_id: meetingId,
requester_id: 'nodeJSapp',
whiteboard_id: slideId,
reply_to: `${meetingId}/nodeJSapp`,
whiteboardId: slideId,
};
return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
return RedisPubSub.publish(CHANNEL, EVENT_NAME, meetingId, payload, header);
};
const SUPPORTED_TYPES = [SVG, PNG];
@ -42,39 +42,35 @@ const fetchImageSizes = imageUri =>
export default function addSlide(meetingId, presentationId, slide) {
check(presentationId, String);
check(slide, Object);
check(slide, {
id: String,
num: Number,
thumbUri: String,
swfUri: String,
txtUri: String,
svgUri: String,
current: Boolean,
xOffset: Number,
yOffset: Number,
widthRatio: Number,
heightRatio: Number,
});
const selector = {
meetingId,
presentationId,
'slide.id': slide.id,
id: slide.id,
};
const imageUri = slide.svgUri || slide.pngUri;
const modifier = {
$set: {
meetingId,
presentationId,
slide: {
id: slide.id,
num: slide.num,
thumb_uri: slide.thumbUri,
swf_uri: slide.swfUri,
txt_uri: slide.txtUri,
// svgUri or pngUri is represented by imageUri
current: slide.current,
x_offset: slide.xOffset,
y_offset: slide.yOffset,
width_ratio: slide.widthRatio,
height_ratio: slide.heightRatio,
img_uri: imageUri,
// width and height are additionally calculated, they are not received
width: slide.width,
height: slide.height,
},
},
$set: Object.assign(
{ meetingId },
{ presentationId },
flat(slide, { safe: true }),
),
};
const cb = (err, numChanged) => {
@ -90,15 +86,13 @@ export default function addSlide(meetingId, presentationId, slide) {
return Logger.info(`Added slide id=${slide.id} to presentation=${presentationId}`);
}
if (numChanged) {
return Logger.info(`Upserted slide id=${slide.id} to presentation=${presentationId}`);
}
return Logger.info(`Upserted slide id=${slide.id} to presentation=${presentationId}`);
};
return fetchImageSizes(imageUri)
.then(({ width, height }) => {
modifier.$set.slide.width = width;
modifier.$set.slide.height = height;
modifier.$set.width = width;
modifier.$set.height = height;
return Slides.upsert(selector, modifier, cb);
})

View File

@ -11,10 +11,10 @@ export default function changeCurrentSlide(meetingId, presentationId, slideId) {
selector: {
meetingId,
presentationId,
'slide.current': true,
current: true,
},
modifier: {
$set: { 'slide.current': false },
$set: { current: false },
},
callback: (err) => {
if (err) {
@ -29,10 +29,10 @@ export default function changeCurrentSlide(meetingId, presentationId, slideId) {
selector: {
meetingId,
presentationId,
'slide.id': slideId,
id: slideId,
},
modifier: {
$set: { 'slide.current': true },
$set: { current: true },
},
callback: (err) => {
if (err) {

View File

@ -1,7 +1,7 @@
import Slides from '/imports/api/2.0/slides';
import Logger from '/imports/startup/server/logger';
import { check } from 'meteor/check';
import clearShapesWhiteboard from '/imports/api/2.0/shapes/server/modifiers/clearShapesWhiteboard';
import clearAnnotations from '/imports/api/2.0/annotations/server/modifiers/clearAnnotations';
export default function clearSlidesPresentation(meetingId, presentationId) {
check(meetingId, String);
@ -12,18 +12,16 @@ export default function clearSlidesPresentation(meetingId, presentationId) {
presentationId,
};
const whiteboardIds = Slides.find(selector).map(row => row.slide.id);
const whiteboardIds = Slides.find(selector).map(row => row.id);
const cb = (err, numChanged) => {
const cb = (err) => {
if (err) {
return Logger.error(`Removing Slides from collection: ${err}`);
}
if (numChanged) {
whiteboardIds.forEach(whiteboardId => clearShapesWhiteboard(meetingId, whiteboardId));
whiteboardIds.forEach(whiteboardId => clearAnnotations(meetingId, whiteboardId));
return Logger.info(`Removed Slides where presentationId=${presentationId}`);
}
return Logger.info(`Removed Slides where presentationId=${presentationId}`);
};
return Slides.remove(selector, cb);

View File

@ -6,20 +6,20 @@ export default function resizeSlide(meetingId, slide) {
check(meetingId, String);
const { presentationId } = slide;
const { pageId } = slide;
const { pageId, widthRatio, heightRatio, xOffset, yOffset } = slide;
const selector = {
meetingId,
presentationId,
'slide.id': pageId,
id: pageId,
};
const modifier = {
$set: {
'slide.width_ratio': slide.widthRatio,
'slide.height_ratio': slide.heightRatio,
'slide.x_offset': slide.xOffset,
'slide.y_offset': slide.yOffset,
widthRatio,
heightRatio,
xOffset,
yOffset,
},
};

View File

@ -15,8 +15,8 @@ export default function handleEmojiStatus({ body }, meetingId) {
const modifier = {
$set: {
'user.set_emoji_time': (new Date()).getTime(),
'user.emoji': emoji,
emojiTime: (new Date()).getTime(),
emoji,
},
};

View File

@ -6,15 +6,15 @@ const unassignCurrentPresenter = (meetingId, presenterId) => {
const selector = {
meetingId,
userId: { $ne: presenterId },
'user.presenter': true,
presenter: true,
};
const modifier = {
$set: {
'user.presenter': false,
presenter: false,
},
$pop: {
'user.roles': 'presenter',
roles: 'presenter',
},
};
@ -41,10 +41,10 @@ export default function handlePresenterAssigned({ body }, meetingId) {
const modifier = {
$set: {
'user.presenter': true,
presenter: true,
},
$push: {
'user.roles': 'presenter',
roles: 'presenter',
},
};

View File

@ -33,7 +33,7 @@ export default function assignPresenter(credentials, userId) {
const payload = {
newPresenterId: userId,
newPresenterName: User.user.name,
newPresenterName: User.name,
assignedBy: requesterUserId,
requesterId: requesterUserId,
};

View File

@ -28,7 +28,7 @@ export default function userLeaving(credentials, userId) {
'user-not-found', `Could not find ${userId} in ${meetingId}: cannot complete userLeaving`);
}
if (User.user.connection_status === OFFLINE_CONNECTION_STATUS) {
if (User.connectionStatus === OFFLINE_CONNECTION_STATUS) {
return null;
}

View File

@ -1,12 +1,26 @@
import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger';
import Users from '/imports/api/2.0/users';
import flat from 'flat';
import addVoiceUser from '/imports/api/2.0/voice-users/server/modifiers/addVoiceUser';
export default function addUser(meetingId, user) {
check(user, Object);
check(meetingId, String);
check(user, {
intId: String,
extId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
waitingForAcceptance: Boolean,
emoji: String,
presenter: Boolean,
locked: Boolean,
avatar: String,
});
const userId = user.intId;
check(userId, String);
@ -42,23 +56,13 @@ export default function addUser(meetingId, user) {
userRoles = userRoles.filter(Boolean);
const modifier = {
$set: {
meetingId,
userId,
'user.connection_status': 'online',
'user.userid': userId,
'user.extId': user.extId,
'user.role': user.role,
'user.roles': userRoles,
'user.name': user.name,
'user._sort_name': user.name.trim().toLowerCase(),
'user.avatarURL': user.avatar,
'user.set_emoji_time': user.set_emoji_time || (new Date()).getTime(),
'user.joiningTime': (new Date()).getTime(),
'user.emoji': user.emoji,
'user.presenter': user.presenter,
'user.locked': user.locked,
},
$set: Object.assign(
{ meetingId },
{ connectionStatus: 'online' },
{ roles: userRoles },
{ sortName: user.name.trim().toLowerCase() },
flat(user),
),
};
addVoiceUser(meetingId, {

View File

@ -16,12 +16,12 @@ export default function removeUser(meetingId, userId) {
const modifier = {
$set: {
'user.connection_status': 'offline',
'user.time_of_joining': 0,
'user.validated': false,
'user.emoji': 'none',
'user.presenter': false,
'user.role': 'VIEWER',
connectionStatus: 'offline',
listenOnly: false,
validated: false,
emoji: 'none',
presenter: false,
role: 'VIEWER',
},
};

View File

@ -22,7 +22,7 @@ export default function setConnectionStatus(meetingId, userId, status = 'online'
const modifier = {
$set: {
'user.connection_status': status,
connectionStatus: status,
},
};

View File

@ -52,7 +52,7 @@ export class Acl {
const containRole = Acl.containsRole(user);
if (containRole) {
const roles = user.user.roles;
const roles = user.roles;
let permissions = {};
roles.forEach((role) => {
@ -66,7 +66,6 @@ export class Acl {
static containsRole(user) {
return Match.test(user, Object) &&
Match.test(user.user, Object) &&
Match.test(user.user.roles, Array);
Match.test(user.roles, Array);
}
}

View File

@ -87,9 +87,7 @@ Base.propTypes = propTypes;
Base.defaultProps = defaultProps;
const SUBSCRIPTIONS_NAME = [
'users2x', 'users', 'chat', 'chat2x', 'cursor', 'cursor2x', 'screenshare', 'meetings', 'meetings2x',
'polls', 'polls2x', 'presentations', 'presentations2x', 'shapes', 'shapes2x', 'slides', 'slides2x', 'captions',
'captions2x', 'breakouts', 'breakouts2x', 'voiceUsers',
'users2x', 'chat2x', 'cursor2x', 'meetings2x', 'polls2x', 'presentations2x', 'annotations', 'slides2x', 'captions2x', 'breakouts2x', 'voiceUsers',
];
const BaseContainer = createContainer(({ params }) => {

View File

@ -11,7 +11,7 @@ const getEmojiData = () => {
const userEmojiStatus = Users.findOne({
meetingId: Auth.meetingID,
userId: Auth.userID,
}).user.emoji;
}).emoji;
return {
userEmojiStatus,

View File

@ -4,7 +4,7 @@ import Users from '/imports/api/2.0/users';
const isUserPresenter = () => Users.findOne({
userId: Auth.userID,
}).user.presenter;
}).presenter;
export default {
isUserPresenter,

View File

@ -6,7 +6,7 @@ import Meetings from '/imports/api/2.0/meetings';
const init = () => {
const userId = Auth.userID;
const User = Users.findOne({ userId });
const username = User.user.name;
const username = User.name;
const Meeting = Meetings.findOne({ meetingId: User.meetingId });
const voiceBridge = Meeting.voiceProp.voiceConf;

View File

@ -30,7 +30,7 @@ const getUser = (userID) => {
return null;
}
return mapUser(user.user);
return mapUser(user);
};
const mapMessage = (messagePayload) => {

View File

@ -3,7 +3,7 @@ import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service'
const getPresentationInfo = () => {
const currentPresentation = Presentations.findOne({
'presentation.current': true,
current: true,
});
return {

View File

@ -7,12 +7,12 @@ const mapPolls = function () {
return { pollExists: false };
}
const amIRequester = poll.requester != 'userId';
const amIRequester = poll.requester !== 'userId';
return {
poll: {
answers: poll.poll.answers,
pollId: poll.poll.id,
answers: poll.answers,
pollId: poll.id,
},
pollExists: true,
amIRequester,

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import PollingContainer from '/imports/ui/components/polling/container';
import ShapeGroupContainer from '../whiteboard/shape-group/container';
import AnnotationGroupContainer from '../whiteboard/annotation-group/container';
import Cursor from './cursor/component';
import PresentationToolbarContainer from './presentation-toolbar/container';
import Slide from './slide/component';
@ -17,11 +17,11 @@ export default class PresentationArea extends React.Component {
let slideObj = this.props.currentSlide;
if (this.props.currentSlide) {
slideObj = this.props.currentSlide.slide;
const x = -slideObj.x_offset * 2 * slideObj.width / 100;
const y = -slideObj.y_offset * 2 * slideObj.height / 100;
const viewBoxWidth = slideObj.width * slideObj.width_ratio / 100;
const viewBoxHeight = slideObj.height * slideObj.height_ratio / 100;
slideObj = this.props.currentSlide;
const x = -slideObj.xOffset * 2 * slideObj.width / 100;
const y = -slideObj.yOffset * 2 * slideObj.height / 100;
const viewBoxWidth = slideObj.width * slideObj.widthRatio / 100;
const viewBoxHeight = slideObj.height * slideObj.heightRatio / 100;
return (
<CSSTransitionGroup
transitionName={{
@ -54,7 +54,7 @@ export default class PresentationArea extends React.Component {
</defs>
<g clipPath="url(#viewBox)">
<Slide id="slideComponent" currentSlide={this.props.currentSlide} />
<ShapeGroupContainer
<AnnotationGroupContainer
width={slideObj.width}
height={slideObj.height}
whiteboardId={slideObj.id}
@ -65,7 +65,7 @@ export default class PresentationArea extends React.Component {
viewBoxHeight={viewBoxHeight}
viewBoxX={x}
viewBoxY={y}
widthRatio={slideObj.width_ratio}
widthRatio={slideObj.widthRatio}
cursorX={this.props.cursor.x}
cursorY={this.props.cursor.y}
/>
@ -82,7 +82,7 @@ export default class PresentationArea extends React.Component {
if (this.props.currentSlide) {
return (
<PresentationToolbarContainer
currentSlideNum={this.props.currentSlide.slide.num}
currentSlideNum={this.props.currentSlide.num}
presentationId={this.props.currentSlide.presentationId}
/>
);

View File

@ -4,7 +4,7 @@ import Slides from '/imports/api/2.0/slides';
import { makeCall } from '/imports/ui/services/api';
const getSlideData = (params) => {
const { currentSlideNum, presentationId } = params;
const { presentationId } = params;
// Get userId and meetingId
const userId = AuthSingleton.userID;
@ -17,8 +17,8 @@ const getSlideData = (params) => {
});
let userIsPresenter;
if (currentUser && currentUser.user) {
userIsPresenter = currentUser.user.presenter;
if (currentUser) {
userIsPresenter = currentUser.presenter;
}
// Get total number of slides in this presentation
@ -35,19 +35,19 @@ const getSlideData = (params) => {
const previousSlide = (currentSlideNum) => {
if (currentSlideNum > 1) {
makeCall('switchSlideMessage', currentSlideNum - 1);
makeCall('switchSlide', currentSlideNum - 1);
}
};
const nextSlide = (currentSlideNum, numberOfSlides) => {
if (currentSlideNum < numberOfSlides) {
makeCall('switchSlideMessage', currentSlideNum + 1);
makeCall('switchSlide', currentSlideNum + 1);
}
};
const skipToSlide = (event) => {
const requestedSlideNum = parseInt(event.target.value);
makeCall('switchSlideMessage', requestedSlideNum);
makeCall('switchSlide', requestedSlideNum);
};
export default {

View File

@ -5,7 +5,7 @@ import Users from '/imports/api/2.0/users';
import Auth from '/imports/ui/services/auth';
const getCurrentPresentation = () => Presentations.findOne({
'presentation.current': true,
current: true,
});
const getCurrentSlide = () => {
@ -16,8 +16,8 @@ const getCurrentSlide = () => {
}
return Slides.findOne({
presentationId: currentPresentation.presentation.id,
'slide.current': true,
presentationId: currentPresentation.id,
current: true,
});
};
@ -26,8 +26,8 @@ const getCurrentCursor = () => Cursor.findOne({});
const isPresenter = () => {
const currentUser = Users.findOne({ userId: Auth.userID });
if (currentUser && currentUser.user) {
return currentUser.user.presenter;
if (currentUser) {
return currentUser.presenter;
}
return false;

View File

@ -7,6 +7,9 @@ export default class Slide extends React.Component {
}
render() {
const imageUri = this.props.currentSlide.svgUri || this.props.currentSlide.pngUri;
return (
<g>
{this.props.currentSlide ?
@ -14,19 +17,20 @@ export default class Slide extends React.Component {
<rect
x="0"
y="0"
width={this.props.currentSlide.slide.width}
height={this.props.currentSlide.slide.height}
width={this.props.currentSlide.width}
height={this.props.currentSlide.height}
fill="white"
/>
<image
x="0" y="0"
width={this.props.currentSlide.slide.width}
height={this.props.currentSlide.slide.height}
xlinkHref={this.props.currentSlide.slide.img_uri}
x="0"
y="0"
width={this.props.currentSlide.width}
height={this.props.currentSlide.height}
xlinkHref={imageUri}
strokeWidth="0.8"
/>
</g>
: null }
: null}
</g>
);
}

View File

@ -17,7 +17,7 @@ const getClosedCaptionLocales = () => {
const getUserRoles = () => {
const user = Users.findOne({
userId: Auth.userID,
}).user;
});
return user.role;
};

View File

@ -133,20 +133,19 @@ const sortChats = (a, b) => {
};
const userFindSorting = {
'user.set_emoji_time': 1,
'user.role': 1,
'user.phone_user': 1,
'user._sort_name': 1,
'user.userid': 1,
emojiTime: 1,
role: 1,
phoneUser: 1,
sortName: 1,
userId: 1,
};
const getUsers = () => {
const users = Users
.find({ 'user.connection_status': 'online' }, userFindSorting)
.find({ connectionStatus: 'online' }, userFindSorting)
.fetch();
return users
.map(u => u.user)
.map(mapUser)
.sort(sortUsers);
};
@ -164,8 +163,7 @@ const getOpenChats = (chatID) => {
openChats = _.uniq(openChats);
openChats = Users
.find({ 'user.userid': { $in: openChats } })
.map(u => u.user)
.find({ userId: { $in: openChats } })
.map(mapUser)
.map((op) => {
const openChat = op;
@ -207,9 +205,9 @@ const getOpenChats = (chatID) => {
const getCurrentUser = () => {
const currentUserId = Auth.userID;
const currentUser = Users.findOne({ 'user.userid': currentUserId });
const currentUser = Users.findOne({ userId: currentUserId });
return (currentUser) ? mapUser(currentUser.user) : null;
return (currentUser) ? mapUser(currentUser) : null;
};
export default {

View File

@ -0,0 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';
import Ellipse from '../annotations/ellipse/component.jsx';
import Line from '../annotations/line/component.jsx';
import Poll from '../annotations/poll/component.jsx';
import Rectangle from '../annotations/rectangle/component.jsx';
import Text from '../annotations/text/component.jsx';
import Triangle from '../annotations/triangle/component.jsx';
import Pencil from '../annotations/pencil/component.jsx';
export default class WhiteboardAnnotationModel extends React.Component {
constructor(props) {
super(props);
}
render() {
const Component = this.props.annotations[this.props.annotation.annotationType];
if (Component != null) {
return (
<Component
annotation={this.props.annotation.annotationInfo}
widthRatio={this.props.widthRatio}
heightRatio={this.props.heightRatio}
slideWidth={this.props.slideWidth}
slideHeight={this.props.slideHeight}
/>
);
}
return (
<g />
);
}
}
WhiteboardAnnotationModel.defaultProps = {
annotations: {
ellipse: Ellipse,
line: Line,
poll_result: Poll,
rectangle: Rectangle,
text: Text,
triangle: Triangle,
pencil: Pencil,
},
};

View File

@ -1,34 +1,34 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import WhiteboardShapeModel from '../shape-factory/component';
import WhiteboardAnnotationModel from '../annotation-factory/component';
const propTypes = {
// initial width and height of the slide are required to calculate the coordinates for each shape
// initial width and height of the slide are required to calculate the coordinates for each annotation
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
// array of shapes, optional
shapes: PropTypes.array,
// array of annotations, optional
annotations: PropTypes.array,
};
export default class ShapeGroup extends React.Component {
export default class AnnotationGroup extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
shapes,
annotations,
width,
height,
} = this.props;
return (
<g>
{shapes ? shapes.map(shape =>
(<WhiteboardShapeModel
shape={shape.shape}
key={shape.shape.id}
{annotations ? annotations.map(annotation =>
(<WhiteboardAnnotationModel
annotation={annotation}
key={annotation.id}
slideWidth={width}
slideHeight={height}
/>),
@ -39,4 +39,4 @@ export default class ShapeGroup extends React.Component {
}
}
ShapeGroup.propTypes = propTypes;
AnnotationGroup.propTypes = propTypes;

View File

@ -2,30 +2,30 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createContainer } from 'meteor/react-meteor-data';
import ShapeGroupService from './service';
import ShapeGroup from './component';
import AnnotationGroupService from './service';
import AnnotationGroup from './component';
const propTypes = {
// the id is required to fetch the shapes
// the id is required to fetch the annotations
whiteboardId: PropTypes.string.isRequired,
// initial width and height of the slide are required to calculate the coordinates for each shape
// initial width and height of the slide are required to calculate the coordinates for each annotation
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
// array of shapes, optional
shapes: PropTypes.array,
// array of annotations, optional
annotations: PropTypes.array,
};
class ShapeGroupContainer extends React.Component {
class AnnotationGroupContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<ShapeGroup
shapes={this.props.shapes}
<AnnotationGroup
annotations={this.props.annotations}
width={this.props.width}
height={this.props.height}
/>
@ -35,13 +35,13 @@ class ShapeGroupContainer extends React.Component {
export default createContainer((params) => {
const { whiteboardId, width, height } = params;
const shapes = ShapeGroupService.getCurrentShapes(whiteboardId);
const annotations = AnnotationGroupService.getCurrentAnnotations(whiteboardId);
return {
shapes,
annotations,
width,
height,
};
}, ShapeGroupContainer);
}, AnnotationGroupContainer);
ShapeGroupContainer.propTypes = propTypes;
AnnotationGroupContainer.propTypes = propTypes;

View File

@ -0,0 +1,15 @@
import Annotations from '/imports/api/2.0/annotations';
const getCurrentAnnotations = (whiteboardId) => {
if (!whiteboardId) {
return null;
}
return Annotations.find({
whiteboardId,
}).fetch();
};
export default {
getCurrentAnnotations,
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class EllipseDrawComponent extends React.Component {
constructor(props) {
@ -10,10 +10,10 @@ export default class EllipseDrawComponent extends React.Component {
getCoordinates() {
// x1 and y1 - coordinates of the ellipse's top left corner
// x2 and y2 - coordinates of the ellipse's bottom right corner
const x1 = this.props.shape.points[0];
const y1 = this.props.shape.points[1];
const x2 = this.props.shape.points[2];
const y2 = this.props.shape.points[3];
const x1 = this.props.annotation.points[0];
const y1 = this.props.annotation.points[1];
const x2 = this.props.annotation.points[2];
const y2 = this.props.annotation.points[3];
// rx - horizontal radius
// ry - vertical radius
@ -42,8 +42,8 @@ export default class EllipseDrawComponent extends React.Component {
rx={results.rx}
ry={results.ry}
fill="none"
stroke={ShapeHelpers.formatColor(this.props.shape.color)}
strokeWidth={this.props.shape.thickness}
stroke={AnnotationHelpers.formatColor(this.props.annotation.color)}
strokeWidth={this.props.annotation.thickness}
style={this.props.style}
/>
);

View File

@ -7,7 +7,7 @@ const zoomStroke = (thickness, widthRatio, heightRatio) => {
};
const formatColor = (color) => {
// let color = this.props.shape.shape.shape.color;
// let color = this.props.annotation.annotation.annotation.color;
if (!color) {
color = '0'; // default value
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class LineDrawComponent extends React.Component {
constructor(props) {
@ -8,10 +8,10 @@ export default class LineDrawComponent extends React.Component {
}
getCoordinates() {
const x1 = this.props.shape.points[0] / 100 * this.props.slideWidth;
const y1 = this.props.shape.points[1] / 100 * this.props.slideHeight;
const x2 = this.props.shape.points[2] / 100 * this.props.slideWidth;
const y2 = this.props.shape.points[3] / 100 * this.props.slideHeight;
const x1 = this.props.annotation.points[0] / 100 * this.props.slideWidth;
const y1 = this.props.annotation.points[1] / 100 * this.props.slideHeight;
const x2 = this.props.annotation.points[2] / 100 * this.props.slideWidth;
const y2 = this.props.annotation.points[3] / 100 * this.props.slideHeight;
return {
x1,
@ -29,10 +29,10 @@ export default class LineDrawComponent extends React.Component {
y1={results.y1}
x2={results.x2}
y2={results.y2}
stroke={ShapeHelpers.formatColor(this.props.shape.color)}
stroke={AnnotationHelpers.formatColor(this.props.annotation.color)}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={this.props.shape.thickness}
strokeWidth={this.props.annotation.thickness}
style={this.props.style}
/>
);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class PencilDrawComponent extends React.Component {
constructor(props) {
@ -10,7 +10,7 @@ export default class PencilDrawComponent extends React.Component {
getCoordinates() {
let i = 2;
let path = '';
const points = this.props.shape.points;
const points = this.props.annotation.points;
if (points && points.length >= 2) {
path = `${path}M${points[0] / 100 * this.props.slideWidth
}, ${points[1] / 100 * this.props.slideHeight}`;
@ -29,9 +29,9 @@ export default class PencilDrawComponent extends React.Component {
return (
<path
fill="none"
stroke={ShapeHelpers.formatColor(this.props.shape.color)}
stroke={AnnotationHelpers.formatColor(this.props.annotation.color)}
d={path}
strokeWidth={this.props.shape.thickness}
strokeWidth={this.props.annotation.thickness}
strokeLinejoin="round"
strokeLinecap="round"
style={this.props.style}

View File

@ -6,7 +6,7 @@ export default class PollDrawComponent extends React.Component {
super(props);
this.state = {
// flag indicating whether we need to continue calculating the sizes or display the shape
// flag indicating whether we need to continue calculating the sizes or display the annotation
prepareToDisplay: true,
// outer (white) rectangle's coordinates and sizes (calculated in componentWillMount)
@ -55,16 +55,16 @@ export default class PollDrawComponent extends React.Component {
// calculating only the parts which have to be done just once and don't require
// rendering / rerendering the text objects
// x1 and y1 - coordinates of the top left corner of the shape
// initial width and height are the width and height of the shape
// x1 and y1 - coordinates of the top left corner of the annotation
// initial width and height are the width and height of the annotation
// all the points are given as percentages of the slide
const x1 = this.props.shape.points[0];
const y1 = this.props.shape.points[1];
const initialWidth = this.props.shape.points[2];
const initialHeight = this.props.shape.points[3];
const x1 = this.props.annotation.points[0];
const y1 = this.props.annotation.points[1];
const initialWidth = this.props.annotation.points[2];
const initialHeight = this.props.annotation.points[3];
// calculating the data for the outer rectangle
// 0.001 is needed to accomodate bottom and right borders of the shape
// 0.001 is needed to accomodate bottom and right borders of the annotation
const x = x1 / 100 * this.props.slideWidth;
const y = y1 / 100 * this.props.slideHeight;
const width = (initialWidth - 0.001) / 100 * this.props.slideWidth;
@ -75,7 +75,7 @@ export default class PollDrawComponent extends React.Component {
const textArray = [];
// counting the total number of votes, finding the biggest number of votes
this.props.shape.result.reduce((previousValue, currentValue, currentIndex, array) => {
this.props.annotation.result.reduce((previousValue, currentValue, currentIndex, array) => {
votesTotal = previousValue + currentValue.numVotes;
if (maxNumVotes < currentValue.numVotes) {
maxNumVotes = currentValue.numVotes;
@ -87,10 +87,10 @@ export default class PollDrawComponent extends React.Component {
// filling the textArray with data to display
// adding value of the iterator to each line needed to create unique
// keys while rendering at the end
const arrayLength = this.props.shape.result.length;
const arrayLength = this.props.annotation.result.length;
for (let i = 0; i < arrayLength; ++i) {
const _tempArray = [];
const _result = this.props.shape.result[i];
const _result = this.props.annotation.result[i];
_tempArray.push(_result.key, `${_result.numVotes}`);
if (votesTotal === 0) {
_tempArray.push('0%');
@ -158,9 +158,9 @@ export default class PollDrawComponent extends React.Component {
// calculating the font size in this if / else block
if (this.state.fontSizeDirection != 0) {
const key = `${this.props.shape.id}_key_${this.state.currentLine}`;
const votes = `${this.props.shape.id}_votes_${this.state.currentLine}`;
const percent = `${this.props.shape.id}_percent_${this.state.currentLine}`;
const key = `${this.props.annotation.id}_key_${this.state.currentLine}`;
const votes = `${this.props.annotation.id}_votes_${this.state.currentLine}`;
const percent = `${this.props.annotation.id}_percent_${this.state.currentLine}`;
const keySizes = findDOMNode(this[key]).getBBox();
const voteSizes = findDOMNode(this[votes]).getBBox();
const percSizes = findDOMNode(this[percent]).getBBox();
@ -214,8 +214,8 @@ export default class PollDrawComponent extends React.Component {
let maxRightWidth = 0;
maxLineHeight = 0;
for (let i = 0; i < this.state.textArray.length; ++i) {
const key = `${this.props.shape.id}_key_${i}`;
const percent = `${this.props.shape.id}_percent_${i}`;
const key = `${this.props.annotation.id}_key_${i}`;
const percent = `${this.props.annotation.id}_percent_${i}`;
const keySizes = findDOMNode(this[key]).getBBox();
const percSizes = findDOMNode(this[percent]).getBBox();
@ -236,7 +236,7 @@ export default class PollDrawComponent extends React.Component {
}
}
const digitRef = `${this.props.shape.id}_digit`;
const digitRef = `${this.props.annotation.id}_digit`;
const maxDigitWidth = findDOMNode(this[digitRef]).getBBox().width;
const maxDigitHeight = findDOMNode(this[digitRef]).getBBox().height;
@ -293,10 +293,10 @@ export default class PollDrawComponent extends React.Component {
let yNumVotes = this.state.innerRect.y + verticalPadding - magicNumber;
const extendedTextArray = [];
for (let i = 0; i < this.state.textArray.length; i++) {
if (this.state.maxNumVotes == 0 || this.props.shape.result[i].numVotes === 0) {
if (this.state.maxNumVotes == 0 || this.props.annotation.result[i].numVotes === 0) {
barWidth = 1;
} else {
barWidth = this.props.shape.result[i].numVotes / this.state.maxNumVotes * maxBarWidth;
barWidth = this.props.annotation.result[i].numVotes / this.state.maxNumVotes * maxBarWidth;
}
// coordinates and color of the text inside the line bar
@ -320,30 +320,30 @@ export default class PollDrawComponent extends React.Component {
}
extendedTextArray[i] =
{
key: `${this.props.shape.id}_${this.state.textArray[i][3]}`,
keyColumn: {
keyString: this.state.textArray[i][0],
xLeft,
yLeft,
},
barColumn: {
votesString: this.state.textArray[i][1],
xBar,
yBar,
barWidth,
barHeight,
yNumVotes,
xNumVotes,
color,
numVotes: this.props.shape.result[i].numVotes,
},
percentColumn: {
xRight,
yRight,
percentString: this.state.textArray[i][2],
},
};
{
key: `${this.props.annotation.id}_${this.state.textArray[i][3]}`,
keyColumn: {
keyString: this.state.textArray[i][0],
xLeft,
yLeft,
},
barColumn: {
votesString: this.state.textArray[i][1],
xBar,
yBar,
barWidth,
barHeight,
yNumVotes,
xNumVotes,
color,
numVotes: this.props.annotation.result[i].numVotes,
},
percentColumn: {
xRight,
yRight,
percentString: this.state.textArray[i][2],
},
};
// changing the Y coordinate for all the objects
yBar = yBar + barHeight + verticalPadding;
@ -447,11 +447,11 @@ export default class PollDrawComponent extends React.Component {
renderLine(line) {
// this func just renders the strings for one line
return (
<g key={`${this.props.shape.id}_line_${line[3]}`}>
<g key={`${this.props.annotation.id}_line_${line[3]}`}>
<text
fontFamily="Arial"
fontSize={this.state.calcFontSize}
ref={(ref) => { this[`${this.props.shape.id}_key_${line[3]}`] = ref; }}
ref={(ref) => { this[`${this.props.annotation.id}_key_${line[3]}`] = ref; }}
>
<tspan>
{line[0]}
@ -460,7 +460,7 @@ export default class PollDrawComponent extends React.Component {
<text
fontFamily="Arial"
fontSize={this.state.calcFontSize}
ref={(ref) => { this[`${this.props.shape.id}_votes_${line[3]}`] = ref; }}
ref={(ref) => { this[`${this.props.annotation.id}_votes_${line[3]}`] = ref; }}
>
<tspan>
{line[1]}
@ -469,7 +469,7 @@ export default class PollDrawComponent extends React.Component {
<text
fontFamily="Arial"
fontSize={this.state.calcFontSize}
ref={(ref) => { this[`${this.props.shape.id}_percent_${line[3]}`] = ref; }}
ref={(ref) => { this[`${this.props.annotation.id}_percent_${line[3]}`] = ref; }}
>
<tspan>
{line[2]}
@ -495,7 +495,7 @@ export default class PollDrawComponent extends React.Component {
<text
fontFamily="Arial"
fontSize={this.state.calcFontSize}
ref={(ref) => { this[`${this.props.shape.id}_digit`] = ref; }}
ref={(ref) => { this[`${this.props.annotation.id}_digit`] = ref; }}
>
<tspan>
0

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class RectangleDrawComponent extends React.Component {
constructor(props) {
@ -8,23 +8,23 @@ export default class RectangleDrawComponent extends React.Component {
}
getCoordinates() {
// x1 and y1 are the coordinates of the top left corner of the shape
// x2 and y2 are the coordinates of the bottom right corner of the shape
let x1 = this.props.shape.points[0];
let y1 = this.props.shape.points[1];
let x2 = this.props.shape.points[2];
let y2 = this.props.shape.points[3];
// 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 = this.props.annotation.points[0];
let y1 = this.props.annotation.points[1];
let x2 = this.props.annotation.points[2];
let y2 = this.props.annotation.points[3];
// Presenter pulled rectangle to the left
if (x2 < x1) {
x1 = this.props.shape.points[2];
x2 = this.props.shape.points[0];
x1 = this.props.annotation.points[2];
x2 = this.props.annotation.points[0];
}
// Presenter pulled Rectangle to the top
if (y2 < y1) {
y1 = this.props.shape.points[3];
y2 = this.props.shape.points[1];
y1 = this.props.annotation.points[3];
y2 = this.props.annotation.points[1];
}
const x = x1 / 100 * this.props.slideWidth;
@ -51,8 +51,8 @@ export default class RectangleDrawComponent extends React.Component {
rx="1"
ry="1"
fill="none"
stroke={ShapeHelpers.formatColor(this.props.shape.color)}
strokeWidth={this.props.shape.thickness}
stroke={AnnotationHelpers.formatColor(this.props.annotation.color)}
strokeWidth={this.props.annotation.thickness}
style={this.props.style}
/>
);

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class TextDrawComponent extends React.Component {
constructor(props) {
@ -8,14 +8,14 @@ export default class TextDrawComponent extends React.Component {
}
getCoordinates() {
const x = this.props.shape.x / 100 * this.props.slideWidth;
const y = this.props.shape.y / 100 * this.props.slideHeight;
const width = this.props.shape.textBoxWidth / 100 * this.props.slideWidth;
const height = this.props.shape.textBoxHeight / 100 * this.props.slideHeight;
const fontColor = ShapeHelpers.formatColor(this.props.shape.fontColor);
const fontSize = this.props.shape.fontSize;
const calcedFontSize = this.props.shape.calcedFontSize / 100 * this.props.slideHeight;
const text = this.props.shape.text;
const x = this.props.annotation.x / 100 * this.props.slideWidth;
const y = this.props.annotation.y / 100 * this.props.slideHeight;
const width = this.props.annotation.textBoxWidth / 100 * this.props.slideWidth;
const height = this.props.annotation.textBoxHeight / 100 * this.props.slideHeight;
const fontColor = AnnotationHelpers.formatColor(this.props.annotation.fontColor);
const fontSize = this.props.annotation.fontSize;
const calcedFontSize = this.props.annotation.calcedFontSize / 100 * this.props.slideHeight;
const text = this.props.annotation.text;
return {
x,

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShapeHelpers from '../helpers.js';
import AnnotationHelpers from '../helpers.js';
export default class TriangleDrawComponent extends React.Component {
constructor(props) {
@ -10,14 +10,14 @@ export default class TriangleDrawComponent extends React.Component {
getCoordinates() {
let path = '';
// points[0] and points[1] are x and y coordinates of the top left corner of the shape obj
// points[2] and points[3] are x and y coordinates of the bottom right corner of the shape obj
const xBottomLeft = this.props.shape.points[0];
const yBottomLeft = this.props.shape.points[3];
const xBottomRight = this.props.shape.points[2];
const yBottomRight = this.props.shape.points[3];
// points[0] and points[1] are x and y coordinates of the top left corner of the annotation obj
// points[2] and points[3] are x and y coordinates of the bottom right corner of the annotation obj
const xBottomLeft = this.props.annotation.points[0];
const yBottomLeft = this.props.annotation.points[3];
const xBottomRight = this.props.annotation.points[2];
const yBottomRight = this.props.annotation.points[3];
const xTop = ((xBottomRight - xBottomLeft) / 2) + xBottomLeft;
const yTop = this.props.shape.points[1];
const yTop = this.props.annotation.points[1];
path = `${path}M${xTop / 100 * this.props.slideWidth
},${yTop / 100 * this.props.slideHeight
@ -36,9 +36,9 @@ export default class TriangleDrawComponent extends React.Component {
<path
style={this.props.style}
fill="none"
stroke={ShapeHelpers.formatColor(this.props.shape.color)}
stroke={AnnotationHelpers.formatColor(this.props.annotation.color)}
d={path}
strokeWidth={this.props.shape.thickness}
strokeWidth={this.props.annotation.thickness}
strokeLinejoin="round"
/>
);

View File

@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Ellipse from '../shapes/ellipse/component.jsx';
import Line from '../shapes/line/component.jsx';
import Poll from '../shapes/poll/component.jsx';
import Rectangle from '../shapes/rectangle/component.jsx';
import Text from '../shapes/text/component.jsx';
import Triangle from '../shapes/triangle/component.jsx';
import Pencil from '../shapes/pencil/component.jsx';
export default class WhiteboardShapeModel extends React.Component {
constructor(props) {
super(props);
}
render() {
const Component = this.props.shapes[this.props.shape.shape_type];
if (Component != null) {
return (
<Component
shape={this.props.shape.shape}
widthRatio={this.props.widthRatio}
heightRatio={this.props.heightRatio}
slideWidth={this.props.slideWidth}
slideHeight={this.props.slideHeight}
/>
);
}
return (
<g />
);
}
}
WhiteboardShapeModel.defaultProps = {
shapes: {
ellipse: Ellipse,
line: Line,
poll_result: Poll,
rectangle: Rectangle,
text: Text,
triangle: Triangle,
pencil: Pencil,
},
};

View File

@ -1,15 +0,0 @@
import Shapes from '/imports/api/2.0/shapes';
const getCurrentShapes = (whiteboardId) => {
if (!whiteboardId) {
return null;
}
return Shapes.find({
whiteboardId,
}).fetch();
};
export default {
getCurrentShapes,
};

View File

@ -6,26 +6,26 @@ const ROLE_MODERATOR = USER_CONFIG.role_moderator;
const mapUser = (user) => {
const userId = Auth.userID;
const voiceUser = VoiceUsers.findOne({ intId: user.userid });
const voiceUser = VoiceUsers.findOne({ intId: user.userId });
const { muted, talking, listenOnly } = voiceUser;
const mappedUser = {
id: user.userid,
id: user.userId,
name: user.name,
emoji: {
status: user.emoji,
changedAt: user.set_emoji_time,
changedAt: user.emojiTime,
},
isPresenter: user.presenter,
isModerator: user.role === ROLE_MODERATOR,
isCurrent: user.userid === userId,
isCurrent: user.userId === userId,
isVoiceUser: listenOnly || talking,
isMuted: muted,
isTalking: talking,
isListenOnly: listenOnly,
isSharingWebcam: 0,
isPhoneUser: user.phone_user,
isOnline: user.connection_status === 'online',
isOnline: user.connectionStatus === 'online',
isLocked: user.locked,
};

View File

@ -8,7 +8,7 @@ acl:
- 'polls'
- 'chat'
- 'presentations'
- 'shapes'
- 'annotations'
- 'slides'
- 'captions'
- 'breakouts'
@ -29,4 +29,3 @@ acl:
methods:
- 'assignPresenter'
- 'switchSlide'
- 'switchSlideMessage'

View File

@ -1,20 +1,9 @@
import '/imports/startup/server';
import '/imports/api/1.1/chat/server';
import '/imports/api/1.1/cursor/server';
import '/imports/api/1.1/deskshare/server';
import '/imports/api/1.1/meetings/server';
import '/imports/api/1.1/polls/server';
import '/imports/api/1.1/breakouts/server';
import '/imports/api/1.1/presentations/server';
import '/imports/api/1.1/shapes/server';
import '/imports/api/1.1/slides/server';
import '/imports/api/1.1/captions/server';
import '/imports/api/1.1/users/server';
// 2x
import '/imports/api/2.0/meetings/server';
import '/imports/api/2.0/users/server';
import '/imports/api/2.0/shapes/server';
import '/imports/api/2.0/annotations/server';
import '/imports/api/2.0/cursor/server';
import '/imports/api/2.0/polls/server';
import '/imports/api/2.0/captions/server';