Support "closed" polls whose votes are not visible until they are ended (#7842)

This commit is contained in:
Andy Balaam 2022-02-21 10:21:35 +00:00 committed by GitHub
parent eca64d776a
commit f1e1b7be86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 248 additions and 11 deletions

View file

@ -16,7 +16,14 @@ limitations under the License.
import React, { ChangeEvent, createRef } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { IPartialEvent, M_POLL_KIND_DISCLOSED, M_POLL_START, PollStartEvent } from "matrix-events-sdk";
import {
IPartialEvent,
KNOWN_POLL_KIND,
M_POLL_KIND_DISCLOSED,
M_POLL_KIND_UNDISCLOSED,
M_POLL_START,
PollStartEvent,
} from "matrix-events-sdk";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import ScrollableBaseModal, { IScrollableBaseState } from "../dialogs/ScrollableBaseModal";
@ -43,6 +50,7 @@ interface IState extends IScrollableBaseState {
question: string;
options: string[];
busy: boolean;
kind: KNOWN_POLL_KIND;
autoFocusTarget: FocusTarget;
}
@ -60,6 +68,7 @@ function creatingInitialState(): IState {
question: "",
options: arraySeed("", DEFAULT_NUM_OPTIONS),
busy: false,
kind: M_POLL_KIND_DISCLOSED,
autoFocusTarget: FocusTarget.Topic,
};
}
@ -75,6 +84,7 @@ function editingInitialState(editingMxEvent: MatrixEvent): IState {
question: poll.question.text,
options: poll.answers.map(ans => ans.text),
busy: false,
kind: poll.kind,
autoFocusTarget: FocusTarget.Topic,
};
}
@ -131,7 +141,7 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
const pollStart = PollStartEvent.from(
this.state.question.trim(),
this.state.options.map(a => a.trim()).filter(a => !!a),
M_POLL_KIND_DISCLOSED,
this.state.kind,
).serialize();
if (!this.props.editingMxEvent) {
@ -190,6 +200,26 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
protected renderContent(): React.ReactNode {
return <div className="mx_PollCreateDialog">
<h2>{ _t("Poll type") }</h2>
<Field
element="select"
value={this.state.kind.name}
onChange={this.onPollTypeChange}
>
<option
key={M_POLL_KIND_DISCLOSED.name}
value={M_POLL_KIND_DISCLOSED.name}
>
{ _t("Open poll") }
</option>
<option
key={M_POLL_KIND_UNDISCLOSED.name}
value={M_POLL_KIND_UNDISCLOSED.name}
>
{ _t("Closed poll") }
</option>
</Field>
<p>{ pollTypeNotes(this.state.kind) }</p>
<h2>{ _t("What is your poll question or topic?") }</h2>
<Field
id='poll-topic-input'
@ -242,4 +272,22 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
}
</div>;
}
onPollTypeChange = (e: ChangeEvent<HTMLSelectElement>) => {
this.setState({
kind: (
M_POLL_KIND_DISCLOSED.matches(e.target.value)
? M_POLL_KIND_DISCLOSED
: M_POLL_KIND_UNDISCLOSED
),
});
};
}
function pollTypeNotes(kind: KNOWN_POLL_KIND): string {
if (M_POLL_KIND_DISCLOSED.matches(kind.name)) {
return _t("Voters see results as soon as they have voted");
} else {
return _t("Results are only revealed when you end the poll");
}
}

View file

@ -21,6 +21,7 @@ import { Relations } from 'matrix-js-sdk/src/models/relations';
import { MatrixClient } from 'matrix-js-sdk/src/matrix';
import {
M_POLL_END,
M_POLL_KIND_DISCLOSED,
M_POLL_RESPONSE,
M_POLL_START,
NamespacedValue,
@ -431,6 +432,11 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
const winCount = Math.max(...votes.values());
const userId = this.context.getUserId();
const myVote = userVotes.get(userId)?.answers[0];
const disclosed = M_POLL_KIND_DISCLOSED.matches(poll.kind.name);
// Disclosed: votes are hidden until I vote or the poll ends
// Undisclosed: votes are hidden until poll ends
const showResults = ended || (disclosed && myVote !== undefined);
let totalText: string;
if (ended) {
@ -438,6 +444,8 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
"Final result based on %(count)s votes",
{ count: totalVotes },
);
} else if (!disclosed) {
totalText = _t("Results will be visible when the poll is ended");
} else if (myVote === undefined) {
if (totalVotes === 0) {
totalText = _t("No votes cast");
@ -465,8 +473,7 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
let answerVotes = 0;
let votesText = "";
// Votes are hidden until I vote or the poll ends
if (ended || myVote !== undefined) {
if (showResults) {
answerVotes = votes.get(answer.id) ?? 0;
votesText = _t("%(count)s votes", { count: answerVotes });
}