Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated poll UI: question field and typed responses #11366

Merged
merged 8 commits into from
Feb 18, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import akka.event.Logging
class PollApp2x(implicit val context: ActorContext)
extends GetCurrentPollReqMsgHdlr
with RespondToPollReqMsgHdlr
with RespondToTypedPollReqMsgHdlr
with ShowPollResultReqMsgHdlr
with StartCustomPollReqMsgHdlr
with StartPollReqMsgHdlr
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.bigbluebutton.core.apps.polls

import org.bigbluebutton.common2.domain.SimplePollResultOutVO
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.models.Users2x

trait RespondToTypedPollReqMsgHdlr {
this: PollApp2x =>

def handle(msg: RespondToTypedPollReqMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
log.debug("Received RespondToPollReqMsg {}", RespondToTypedPollReqMsg)

def broadcastPollUpdatedEvent(msg: RespondToTypedPollReqMsg, pollId: String, poll: SimplePollResultOutVO): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(PollUpdatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollUpdatedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

val body = PollUpdatedEvtMsgBody(pollId, poll)
val event = PollUpdatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}

def broadcastUserRespondedToTypedPollRespMsg(msg: RespondToTypedPollReqMsg, pollId: String, answer: String, sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToTypedPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToTypedPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)

val body = UserRespondedToTypedPollRespMsgBody(pollId, msg.header.userId, answer)
val event = UserRespondedToTypedPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}

for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToTypedPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answer, liveMeeting)
} yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)

for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
broadcastUserRespondedToTypedPollRespMsg(msg, pollId, msg.body.answer, presenter.intId)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ trait StartCustomPollReqMsgHdlr extends RightsManagementTrait {
val envelope = BbbCoreEnvelope(PollStartedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollStartedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

val body = PollStartedEvtMsgBody(msg.header.userId, poll.id, poll)
val body = PollStartedEvtMsgBody(msg.header.userId, poll.id, msg.body.pollType, msg.body.question, poll)
val event = PollStartedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
Expand All @@ -29,7 +29,7 @@ trait StartCustomPollReqMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
for {
pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.answers, liveMeeting)
pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.answers, msg.body.question, liveMeeting)
} yield {
broadcastEvent(msg, pvo)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ trait StartPollReqMsgHdlr extends RightsManagementTrait {
val envelope = BbbCoreEnvelope(PollStartedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PollStartedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)

val body = PollStartedEvtMsgBody(msg.header.userId, poll.id, poll)
val body = PollStartedEvtMsgBody(msg.header.userId, poll.id, msg.body.pollType, msg.body.question, poll)
val event = PollStartedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
Expand All @@ -30,7 +30,7 @@ trait StartPollReqMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
for {
pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, liveMeeting)
pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.question, liveMeeting)
} yield {
broadcastEvent(msg, pvo)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.bigbluebutton.core.running.LiveMeeting

object Polls {

def handleStartPollReqMsg(state: MeetingState2x, userId: String, pollId: String, pollType: String,
def handleStartPollReqMsg(state: MeetingState2x, userId: String, pollId: String, pollType: String, question: String,
lm: LiveMeeting): Option[SimplePollOutVO] = {

def createPoll(stampedPollId: String): Option[Poll] = {
Expand Down Expand Up @@ -156,8 +156,18 @@ object Polls {

}

def handleRespondToTypedPollReqMsg(requesterId: String, pollId: String, questionId: Int, answer: String,
lm: LiveMeeting): Option[(String, SimplePollResultOutVO)] = {
for {
poll <- getSimplePollResult(pollId, lm.polls)
pvo <- handleRespondToTypedPoll(poll, requesterId, pollId, questionId, answer, lm)
} yield {
(pollId, pvo)
}
}

def handleStartCustomPollReqMsg(state: MeetingState2x, requesterId: String, pollId: String, pollType: String,
answers: Seq[String], lm: LiveMeeting): Option[SimplePollOutVO] = {
answers: Seq[String], question: String, lm: LiveMeeting): Option[SimplePollOutVO] = {

def createPoll(stampedPollId: String): Option[Poll] = {
val numRespondents: Int = Users2x.numUsers(lm.users2x) - 1 // subtract the presenter
Expand Down Expand Up @@ -227,7 +237,17 @@ object Polls {
} yield {
updatedPoll
}
}

private def handleRespondToTypedPoll(poll: SimplePollResultOutVO, requesterId: String, pollId: String, questionId: Int,
answer: String, lm: LiveMeeting): Option[SimplePollResultOutVO] = {

addQuestionResponse(poll.id, questionId, answer, lm.polls)
for {
updatedPoll <- getSimplePollResult(poll.id, lm.polls)
} yield {
updatedPoll
}
}

private def pollResultToWhiteboardShape(result: SimplePollResultOutVO): scala.collection.immutable.Map[String, Object] = {
Expand Down Expand Up @@ -366,6 +386,14 @@ object Polls {
}
}

def addQuestionResponse(pollId: String, questionID: Int, answer: String, polls: Polls) {
polls.polls.get(pollId) match {
case Some(p) => {
p.addQuestionResponse(questionID, answer)
}
case None =>
}
}
}

object PollType {
Expand All @@ -375,6 +403,7 @@ object PollType {
val CustomPollType = "CUSTOM"
val LetterPollType = "A-"
val NumberPollType = "1-"
val ResponsePollType = "RP"
}

object PollFactory {
Expand All @@ -383,29 +412,29 @@ object PollFactory {
val NumberArray = Array("1", "2", "3", "4", "5", "6")

private def processYesNoPollType(qType: String): Question = {
val answers = new Array[Answer](2)
val answers = new ArrayBuffer[Answer];

answers(0) = new Answer(0, "Yes", Some("Yes"))
answers(1) = new Answer(1, "No", Some("No"))
answers += new Answer(0, "Yes", Some("Yes"))
answers += new Answer(1, "No", Some("No"))

new Question(0, PollType.YesNoPollType, false, None, answers)
}

private def processYesNoAbstentionPollType(qType: String): Question = {
val answers = new Array[Answer](3)
val answers = new ArrayBuffer[Answer]

answers(0) = new Answer(0, "Yes", Some("Yes"))
answers(1) = new Answer(1, "No", Some("No"))
answers(2) = new Answer(2, "Abstention", Some("Abstention"))
answers += new Answer(0, "Yes", Some("Yes"))
answers += new Answer(1, "No", Some("No"))
answers += new Answer(2, "Abstention", Some("Abstention"))

new Question(0, PollType.YesNoAbstentionPollType, false, None, answers)
}

private def processTrueFalsePollType(qType: String): Question = {
val answers = new Array[Answer](2)
val answers = new ArrayBuffer[Answer];

answers(0) = new Answer(0, "True", Some("True"))
answers(1) = new Answer(1, "False", Some("False"))
answers += new Answer(0, "True", Some("True"))
answers += new Answer(1, "False", Some("False"))

new Question(0, PollType.TrueFalsePollType, false, None, answers)
}
Expand All @@ -417,10 +446,9 @@ object PollFactory {
var questionOption: Option[Question] = None

if (numQs > 0 && numQs <= 6) {
val answers = new Array[Answer](numQs)

val answers = new ArrayBuffer[Answer];
for (i <- 0 until numQs) {
answers(i) = new Answer(i, LetterArray(i), Some(LetterArray(i)))
answers += new Answer(i, LetterArray(i), Some(LetterArray(i)))
val question = new Question(0, PollType.LetterPollType, multiResponse, None, answers)
questionOption = Some(question)
}
Expand All @@ -436,20 +464,20 @@ object PollFactory {
var questionOption: Option[Question] = None

if (numQs > 0 && numQs <= 6) {
val answers = new Array[Answer](numQs)
val answers = new ArrayBuffer[Answer];
for (i <- 0 until numQs) {
answers(i) = new Answer(i, NumberArray(i), Some(NumberArray(i)))
answers += new Answer(i, NumberArray(i), Some(NumberArray(i)))
val question = new Question(0, PollType.NumberPollType, multiResponse, None, answers)
questionOption = Some(question)
}
}
questionOption
}

private def buildAnswers(answers: Seq[String]): Array[Answer] = {
val ans = new Array[Answer](answers.length)
private def buildAnswers(answers: Seq[String]): ArrayBuffer[Answer] = {
val ans = new ArrayBuffer[Answer]
for (i <- 0 until answers.length) {
ans(i) = new Answer(i, answers(i), Some(answers(i)))
ans += new Answer(i, answers(i), Some(answers(i)))
}

ans
Expand All @@ -467,6 +495,16 @@ object PollFactory {
questionOption
}

private def processResponsePollType(qType: String): Option[Question] = {
var questionOption: Option[Question] = None

val answers = new ArrayBuffer[Answer]
val question = new Question(0, PollType.ResponsePollType, false, None, answers)
questionOption = Some(question)

questionOption
}

private def createQuestion(qType: String, answers: Option[Seq[String]]): Option[Question] = {

val qt = qType.toUpperCase()
Expand All @@ -484,6 +522,8 @@ object PollFactory {
questionOption = processLetterPollType(qt, false)
} else if (qt.startsWith(PollType.NumberPollType)) {
questionOption = processNumberPollType(qt, false)
} else if (qt.startsWith(PollType.ResponsePollType)) {
questionOption = processResponsePollType(qt)
}

questionOption
Expand Down Expand Up @@ -550,6 +590,14 @@ class Poll(val id: String, val questions: Array[Question], val numRespondents: I
})
}

def addQuestionResponse(questionID: Int, answer: String) {
questions.foreach(q => {
if (q.id == questionID) {
q.addQuestionResponse(answer)
}
})
}

def toPollVO(): PollVO = {
val qvos = new ArrayBuffer[QuestionVO]
questions.foreach(q => {
Expand All @@ -568,7 +616,10 @@ class Poll(val id: String, val questions: Array[Question], val numRespondents: I
}
}

class Question(val id: Int, val questionType: String, val multiResponse: Boolean, val text: Option[String], val answers: Array[Answer]) {
class Question(val id: Int, val questionType: String, val multiResponse: Boolean, val text: Option[String], val answers: ArrayBuffer[Answer]) {
def addAnswer(text: String) {
answers += new Answer(answers.size, text, Some(text))
}

def clear() {
answers.foreach(r => r.clear)
Expand All @@ -588,6 +639,10 @@ class Question(val id: Int, val questionType: String, val multiResponse: Boolean
})
}

def addQuestionResponse(answer: String) {
addAnswer(answer)
}

def toQuestionVO(): QuestionVO = {
val rvos = new ArrayBuffer[AnswerVO]
answers.foreach(answer => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[GetCurrentPollReqMsg](envelope, jsonNode)
case RespondToPollReqMsg.NAME =>
routeGenericMsg[RespondToPollReqMsg](envelope, jsonNode)
case RespondToTypedPollReqMsg.NAME =>
routeGenericMsg[RespondToTypedPollReqMsg](envelope, jsonNode)

// Webcam
case UserBroadcastCamStartMsg.NAME =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ class MeetingActor(
case m: RespondToPollReqMsg =>
pollApp.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.requesterId)
case m: RespondToTypedPollReqMsg =>
pollApp.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.requesterId)

// Breakout
case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ case class PollShowResultEvtMsgBody(userId: String, pollId: String, poll: Simple

object PollStartedEvtMsg { val NAME = "PollStartedEvtMsg" }
case class PollStartedEvtMsg(header: BbbClientMsgHeader, body: PollStartedEvtMsgBody) extends BbbCoreMsg
case class PollStartedEvtMsgBody(userId: String, pollId: String, poll: SimplePollOutVO)
case class PollStartedEvtMsgBody(userId: String, pollId: String, pollType: String, question: String, poll: SimplePollOutVO)

object PollStoppedEvtMsg { val NAME = "PollStoppedEvtMsg" }
case class PollStoppedEvtMsg(header: BbbClientMsgHeader, body: PollStoppedEvtMsgBody) extends BbbCoreMsg
Expand All @@ -34,21 +34,29 @@ object RespondToPollReqMsg { val NAME = "RespondToPollReqMsg" }
case class RespondToPollReqMsg(header: BbbClientMsgHeader, body: RespondToPollReqMsgBody) extends StandardMsg
case class RespondToPollReqMsgBody(requesterId: String, pollId: String, questionId: Int, answerId: Int)

object RespondToTypedPollReqMsg { val NAME = "RespondToTypedPollReqMsg" }
case class RespondToTypedPollReqMsg(header: BbbClientMsgHeader, body: RespondToTypedPollReqMsgBody) extends StandardMsg
case class RespondToTypedPollReqMsgBody(requesterId: String, pollId: String, questionId: Int, answer: String)

object UserRespondedToPollRespMsg { val NAME = "UserRespondedToPollRespMsg" }
case class UserRespondedToPollRespMsg(header: BbbClientMsgHeader, body: UserRespondedToPollRespMsgBody) extends BbbCoreMsg
case class UserRespondedToPollRespMsgBody(pollId: String, userId: String, answerId: Int)

object UserRespondedToTypedPollRespMsg { val NAME = "UserRespondedToTypedPollRespMsg" }
case class UserRespondedToTypedPollRespMsg(header: BbbClientMsgHeader, body: UserRespondedToTypedPollRespMsgBody) extends BbbCoreMsg
case class UserRespondedToTypedPollRespMsgBody(pollId: String, userId: String, answer: String)

object ShowPollResultReqMsg { val NAME = "ShowPollResultReqMsg" }
case class ShowPollResultReqMsg(header: BbbClientMsgHeader, body: ShowPollResultReqMsgBody) extends StandardMsg
case class ShowPollResultReqMsgBody(requesterId: String, pollId: String)

object StartCustomPollReqMsg { val NAME = "StartCustomPollReqMsg" }
case class StartCustomPollReqMsg(header: BbbClientMsgHeader, body: StartCustomPollReqMsgBody) extends StandardMsg
case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, answers: Seq[String])
case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, answers: Seq[String], question: String)

object StartPollReqMsg { val NAME = "StartPollReqMsg" }
case class StartPollReqMsg(header: BbbClientMsgHeader, body: StartPollReqMsgBody) extends StandardMsg
case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String)
case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String, question: String)

object StopPollReqMsg { val NAME = "StopPollReqMsg" }
case class StopPollReqMsg(header: BbbClientMsgHeader, body: StopPollReqMsgBody) extends StandardMsg
Expand Down
2 changes: 2 additions & 0 deletions bigbluebutton-html5/imports/api/polls/server/eventHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import handlePollStopped from './handlers/pollStopped';
import handlePollPublished from './handlers/pollPublished';
import handleUserVoted from './handlers/userVoted';
import handleUserResponded from './handlers/userResponded';
import handleUserTypedResponse from './handlers/userTypedResponse';

RedisPubSub.on('PollShowResultEvtMsg', handlePollPublished);
RedisPubSub.on('PollStartedEvtMsg', handlePollStarted);
RedisPubSub.on('PollStoppedEvtMsg', handlePollStopped);
RedisPubSub.on('PollUpdatedEvtMsg', handleUserVoted);
RedisPubSub.on('UserRespondedToPollRespMsg', handleUserResponded);
RedisPubSub.on('UserRespondedToTypedPollRespMsg', handleUserTypedResponse);