Support "closed" polls whose votes are not visible until they are ended (#7842)
This commit is contained in:
parent
eca64d776a
commit
f1e1b7be86
8 changed files with 248 additions and 11 deletions
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue