add custom polling functionality to smart polls

This commit is contained in:
KDSBrowne 2022-10-17 20:25:20 +00:00
parent 942f75f478
commit 412221c664
3 changed files with 212 additions and 197 deletions

View File

@ -156,7 +156,9 @@ const QuickPollDropdown = (props) => {
intl.formatMessage(intlMessages.falseOptionLabel),
);
const { slideId, quickPollOptions } = parsedSlide;
const {
slideId, quickPollOptions, optionsWithLabels, pollQuestion,
} = parsedSlide;
const quickPolls = getAvailableQuickPolls(
slideId, quickPollOptions, startPoll, pollTypes, layoutContextDispatch,
);
@ -189,7 +191,17 @@ const QuickPollDropdown = (props) => {
tooltipLabel={intl.formatMessage(intlMessages.quickPollLabel)}
onClick={() => {
handleClickQuickPoll(layoutContextDispatch);
startPoll(singlePollType, currentSlide.id, answers, question, multiResponse);
if (singlePollType === 'R-' || singlePollType === 'TF') {
startPoll(singlePollType, currentSlide.id, answers, question, multiResponse);
} else {
startPoll(
pollTypes.Custom,
currentSlide.id,
optionsWithLabels,
pollQuestion,
multiResponse,
);
}
}}
size="lg"
disabled={!!activePoll}

View File

@ -85,7 +85,7 @@ const intlMessages = defineMessages({
pan: {
id: 'app.whiteboard.toolbar.tools.hand',
description: 'presentation toolbar pan label',
}
},
});
class PresentationToolbar extends PureComponent {
@ -95,19 +95,18 @@ class PresentationToolbar extends PureComponent {
this.handleSkipToSlideChange = this.handleSkipToSlideChange.bind(this);
this.change = this.change.bind(this);
this.renderAriaDescs = this.renderAriaDescs.bind(this);
this.switchSlide = this.switchSlide.bind(this);
this.nextSlideHandler = this.nextSlideHandler.bind(this);
this.previousSlideHandler = this.previousSlideHandler.bind(this);
this.fullscreenToggleHandler = this.fullscreenToggleHandler.bind(this);
this.handleSwitchWhiteboardMode =
this.handleSwitchWhiteboardMode.bind(this);
this.switchSlide = this.switchSlide.bind(this);
this.handleSwitchWhiteboardMode = this.handleSwitchWhiteboardMode.bind(this);
}
componentDidMount() {
document.addEventListener('keydown', this.switchSlide);
}
componentDidUpdate(prevProps, prevState) {
componentDidUpdate(prevProps) {
const { zoom, setIsPanning } = this.props;
if (zoom <= HUNDRED_PERCENT && zoom !== prevProps.zoom) setIsPanning();
}
@ -116,28 +115,6 @@ class PresentationToolbar extends PureComponent {
document.removeEventListener('keydown', this.switchSlide);
}
switchSlide(event) {
const { target, which } = event;
const isBody = target.nodeName === 'BODY';
if (isBody) {
switch (which) {
case KEY_CODES.ARROW_LEFT:
case KEY_CODES.PAGE_UP:
this.previousSlideHandler();
break;
case KEY_CODES.ARROW_RIGHT:
case KEY_CODES.PAGE_DOWN:
this.nextSlideHandler();
break;
case KEY_CODES.ENTER:
this.fullscreenToggleHandler();
break;
default:
}
}
}
handleSkipToSlideChange(event) {
const { skipToSlide, podId } = this.props;
const requestedSlideNum = Number.parseInt(event.target.value, 10);
@ -146,18 +123,17 @@ class PresentationToolbar extends PureComponent {
skipToSlide(requestedSlideNum, podId);
}
nextSlideHandler(event) {
const { nextSlide, currentSlideNum, numberOfSlides, podId } = this.props;
if (event) event.currentTarget.blur();
nextSlide(currentSlideNum, numberOfSlides, podId);
}
previousSlideHandler(event) {
const { previousSlide, currentSlideNum, podId } = this.props;
if (event) event.currentTarget.blur();
previousSlide(currentSlideNum, podId);
handleSwitchWhiteboardMode() {
const {
multiUser,
whiteboardId,
removeWhiteboardGlobalAccess,
addWhiteboardGlobalAccess,
} = this.props;
if (multiUser) {
return removeWhiteboardGlobalAccess(whiteboardId);
}
return addWhiteboardGlobalAccess(whiteboardId);
}
fullscreenToggleHandler() {
@ -182,6 +158,44 @@ class PresentationToolbar extends PureComponent {
});
}
nextSlideHandler(event) {
const {
nextSlide, currentSlideNum, numberOfSlides, podId,
} = this.props;
if (event) event.currentTarget.blur();
nextSlide(currentSlideNum, numberOfSlides, podId);
}
previousSlideHandler(event) {
const { previousSlide, currentSlideNum, podId } = this.props;
if (event) event.currentTarget.blur();
previousSlide(currentSlideNum, podId);
}
switchSlide(event) {
const { target, which } = event;
const isBody = target.nodeName === 'BODY';
if (isBody) {
switch (which) {
case KEY_CODES.ARROW_LEFT:
case KEY_CODES.PAGE_UP:
this.previousSlideHandler();
break;
case KEY_CODES.ARROW_RIGHT:
case KEY_CODES.PAGE_DOWN:
this.nextSlideHandler();
break;
case KEY_CODES.ENTER:
this.fullscreenToggleHandler();
break;
default:
}
}
}
change(value) {
const { zoomChanger } = this.props;
zoomChanger(value);
@ -225,26 +239,13 @@ class PresentationToolbar extends PureComponent {
optionList.push(
<option value={i} key={i}>
{intl.formatMessage(intlMessages.goToSlide, { 0: i })}
</option>
</option>,
);
}
return optionList;
}
handleSwitchWhiteboardMode() {
const {
multiUser,
whiteboardId,
removeWhiteboardGlobalAccess,
addWhiteboardGlobalAccess,
} = this.props;
if (multiUser) {
return removeWhiteboardGlobalAccess(whiteboardId);
}
addWhiteboardGlobalAccess(whiteboardId);
}
render() {
const {
currentSlideNum,
@ -261,7 +262,6 @@ class PresentationToolbar extends PureComponent {
startPoll,
currentSlide,
slidePosition,
toolbarWidth,
multiUserSize,
multiUser,
setIsPanning,
@ -276,173 +276,166 @@ class PresentationToolbar extends PureComponent {
const prevSlideAriaLabel = startOfSlides
? intl.formatMessage(intlMessages.previousSlideLabel)
: `${intl.formatMessage(intlMessages.previousSlideLabel)} (${
currentSlideNum <= 1 ? "" : currentSlideNum - 1
})`;
currentSlideNum <= 1 ? '' : currentSlideNum - 1
})`;
const nextSlideAriaLabel = endOfSlides
? intl.formatMessage(intlMessages.nextSlideLabel)
: `${intl.formatMessage(intlMessages.nextSlideLabel)} (${
currentSlideNum >= 1 ? currentSlideNum + 1 : ""
})`;
currentSlideNum >= 1 ? currentSlideNum + 1 : ''
})`;
return (
<Styled.PresentationToolbarWrapper
id="presentationToolbarWrapper"
>
{this.renderAriaDescs()}
{
<div>
{isPollingEnabled ? (
<Styled.QuickPollButton
{...{
currentSlidHasContent,
intl,
amIPresenter,
parseCurrentSlideContent,
startPoll,
currentSlide,
}}
/>
) : null}
</div>
}
{
<Styled.PresentationSlideControls>
<Styled.PrevSlideButton
role="button"
aria-label={prevSlideAriaLabel}
aria-describedby={
startOfSlides ? "noPrevSlideDesc" : "prevSlideDesc"
}
disabled={startOfSlides || !isMeteorConnected}
color="light"
circle
icon="left_arrow"
size="md"
onClick={this.previousSlideHandler}
label={intl.formatMessage(intlMessages.previousSlideLabel)}
hideLabel
data-test="prevSlide"
<div>
{isPollingEnabled ? (
<Styled.QuickPollButton
{...{
currentSlidHasContent,
intl,
amIPresenter,
parseCurrentSlideContent,
startPoll,
currentSlide,
}}
/>
) : null}
</div>
<Styled.PresentationSlideControls>
<Styled.PrevSlideButton
role="button"
aria-label={prevSlideAriaLabel}
aria-describedby={
startOfSlides ? 'noPrevSlideDesc' : 'prevSlideDesc'
}
disabled={startOfSlides || !isMeteorConnected}
color="light"
circle
icon="left_arrow"
size="md"
onClick={this.previousSlideHandler}
label={intl.formatMessage(intlMessages.previousSlideLabel)}
hideLabel
data-test="prevSlide"
/>
<TooltipContainer
title={intl.formatMessage(intlMessages.selectLabel)}
<TooltipContainer
title={intl.formatMessage(intlMessages.selectLabel)}
>
<Styled.SkipSlideSelect
id="skipSlide"
aria-label={intl.formatMessage(intlMessages.skipSlideLabel)}
aria-describedby="skipSlideDesc"
aria-live="polite"
aria-relevant="all"
disabled={!isMeteorConnected}
value={currentSlideNum}
onChange={this.handleSkipToSlideChange}
data-test="skipSlide"
>
<Styled.SkipSlideSelect
id="skipSlide"
aria-label={intl.formatMessage(intlMessages.skipSlideLabel)}
aria-describedby="skipSlideDesc"
aria-live="polite"
aria-relevant="all"
disabled={!isMeteorConnected}
value={currentSlideNum}
onChange={this.handleSkipToSlideChange}
data-test="skipSlide"
>
{this.renderSkipSlideOpts(numberOfSlides)}
</Styled.SkipSlideSelect>
</TooltipContainer>
<Styled.NextSlideButton
role="button"
aria-label={nextSlideAriaLabel}
aria-describedby={
{this.renderSkipSlideOpts(numberOfSlides)}
</Styled.SkipSlideSelect>
</TooltipContainer>
<Styled.NextSlideButton
role="button"
aria-label={nextSlideAriaLabel}
aria-describedby={
endOfSlides ? 'noNextSlideDesc' : 'nextSlideDesc'
}
disabled={endOfSlides || !isMeteorConnected}
color="light"
circle
icon="right_arrow"
size="md"
onClick={this.nextSlideHandler}
label={intl.formatMessage(intlMessages.nextSlideLabel)}
hideLabel
data-test="nextSlide"
/>
</Styled.PresentationSlideControls>
}
{
<Styled.PresentationZoomControls>
<Styled.WBAccessButton
data-test={multiUser ? 'turnMultiUsersWhiteboardOff' : 'turnMultiUsersWhiteboardOn'}
role="button"
aria-label={
disabled={endOfSlides || !isMeteorConnected}
color="light"
circle
icon="right_arrow"
size="md"
onClick={this.nextSlideHandler}
label={intl.formatMessage(intlMessages.nextSlideLabel)}
hideLabel
data-test="nextSlide"
/>
</Styled.PresentationSlideControls>
<Styled.PresentationZoomControls>
<Styled.WBAccessButton
data-test={multiUser ? 'turnMultiUsersWhiteboardOff' : 'turnMultiUsersWhiteboardOn'}
role="button"
aria-label={
multiUser
? intl.formatMessage(intlMessages.toolbarMultiUserOff)
: intl.formatMessage(intlMessages.toolbarMultiUserOn)
}
color="light"
disabled={!isMeteorConnected}
icon={multiUser ? 'multi_whiteboard' : 'whiteboard'}
size="md"
circle
onClick={() => this.handleSwitchWhiteboardMode(!multiUser)}
label={
color="light"
disabled={!isMeteorConnected}
icon={multiUser ? 'multi_whiteboard' : 'whiteboard'}
size="md"
circle
onClick={() => this.handleSwitchWhiteboardMode(!multiUser)}
label={
multiUser
? intl.formatMessage(intlMessages.toolbarMultiUserOff)
: intl.formatMessage(intlMessages.toolbarMultiUserOn)
}
hideLabel
/>
{multiUser ? (
<Styled.MultiUserTool>{multiUserSize}</Styled.MultiUserTool>
) : (
<Styled.MUTPlaceholder />
)}
{!isMobile ? (
<TooltipContainer>
<ZoomTool
slidePosition={slidePosition}
zoomValue={zoom}
currentSlideNum={currentSlideNum}
change={this.change}
minBound={HUNDRED_PERCENT}
maxBound={MAX_PERCENT}
step={STEP}
isMeteorConnected={isMeteorConnected}
/>
</TooltipContainer>
) : null}
<Styled.FitToWidthButton
role="button"
data-test="panButton"
aria-label={intl.formatMessage(intlMessages.pan)}
color="light"
disabled={(zoom <= HUNDRED_PERCENT)}
icon="hand"
size="md"
circle
onClick={setIsPanning}
label={intl.formatMessage(intlMessages.pan)}
hideLabel
panning={isPanning}
/>
<Styled.FitToWidthButton
role="button"
data-test="fitToWidthButton"
aria-describedby={fitToWidth ? 'fitPageDesc' : 'fitWidthDesc'}
aria-label={
hideLabel
/>
{multiUser ? (
<Styled.MultiUserTool>{multiUserSize}</Styled.MultiUserTool>
) : (
<Styled.MUTPlaceholder />
)}
{!isMobile ? (
<TooltipContainer>
<ZoomTool
slidePosition={slidePosition}
zoomValue={zoom}
currentSlideNum={currentSlideNum}
change={this.change}
minBound={HUNDRED_PERCENT}
maxBound={MAX_PERCENT}
step={STEP}
isMeteorConnected={isMeteorConnected}
/>
</TooltipContainer>
) : null}
<Styled.FitToWidthButton
role="button"
data-test="panButton"
aria-label={intl.formatMessage(intlMessages.pan)}
color="light"
disabled={(zoom <= HUNDRED_PERCENT)}
icon="hand"
size="md"
circle
onClick={setIsPanning}
label={intl.formatMessage(intlMessages.pan)}
hideLabel
panning={isPanning}
/>
<Styled.FitToWidthButton
role="button"
data-test="fitToWidthButton"
aria-describedby={fitToWidth ? 'fitPageDesc' : 'fitWidthDesc'}
aria-label={
fitToWidth
? `${intl.formatMessage(
intlMessages.presentationLabel
)} ${intl.formatMessage(intlMessages.fitToPage)}`
intlMessages.presentationLabel,
)} ${intl.formatMessage(intlMessages.fitToPage)}`
: `${intl.formatMessage(
intlMessages.presentationLabel
)} ${intl.formatMessage(intlMessages.fitToWidth)}`
intlMessages.presentationLabel,
)} ${intl.formatMessage(intlMessages.fitToWidth)}`
}
color="light"
disabled={!isMeteorConnected}
icon="fit_to_width"
size="md"
circle
onClick={fitToWidthHandler}
label={fitToWidth
? intl.formatMessage(intlMessages.fitToPage)
: intl.formatMessage(intlMessages.fitToWidth)
}
hideLabel
/>
</Styled.PresentationZoomControls>
}
color="light"
disabled={!isMeteorConnected}
icon="fit_to_width"
size="md"
circle
onClick={fitToWidthHandler}
label={fitToWidth
? intl.formatMessage(intlMessages.fitToPage)
: intl.formatMessage(intlMessages.fitToWidth)}
hideLabel
/>
</Styled.PresentationZoomControls>
</Styled.PresentationToolbarWrapper>
);
}

View File

@ -91,7 +91,13 @@ const parseCurrentSlideContent = (yesValue, noValue, abstentionValue, trueValue,
const pollRegex = /[1-9A-Ia-i][.)].*/g;
let optionsPoll = content.match(pollRegex) || [];
if (optionsPoll) optionsPoll = optionsPoll.map((opt) => `\r${opt[0]}.`);
let optionsWithLabels = [];
if (optionsPoll) {
optionsPoll = optionsPoll.map((opt) => {
optionsWithLabels.push(opt);
return `\r${opt[0]}.`
});
}
optionsPoll.reduce((acc, currentValue) => {
const lastElement = acc[acc.length - 1];
@ -178,9 +184,13 @@ const parseCurrentSlideContent = (yesValue, noValue, abstentionValue, trueValue,
poll,
}));
const pollQuestion = (question?.length > 0 && question[0]) || '';
return {
slideId: currentSlide.id,
quickPollOptions,
optionsWithLabels,
pollQuestion,
};
};