Show user what options they have voted (#11195)
* Add own_votes field to poll results in REST API Fixes #10679 * Display user votes in WebUI * Update styling * Add vote checkmark to public pages
This commit is contained in:
parent
26b810561a
commit
b359974d9b
|
@ -73,8 +73,9 @@ export function normalizePoll(poll) {
|
||||||
|
|
||||||
const emojiMap = makeEmojiMap(normalPoll);
|
const emojiMap = makeEmojiMap(normalPoll);
|
||||||
|
|
||||||
normalPoll.options = poll.options.map(option => ({
|
normalPoll.options = poll.options.map((option, index) => ({
|
||||||
...option,
|
...option,
|
||||||
|
voted: poll.own_votes && poll.own_votes.includes(index),
|
||||||
title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import spring from 'react-motion/lib/spring';
|
||||||
import escapeTextContentForBrowser from 'escape-html';
|
import escapeTextContentForBrowser from 'escape-html';
|
||||||
import emojify from 'mastodon/features/emoji/emoji';
|
import emojify from 'mastodon/features/emoji/emoji';
|
||||||
import RelativeTimestamp from './relative_timestamp';
|
import RelativeTimestamp from './relative_timestamp';
|
||||||
|
import Icon from 'mastodon/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
closed: { id: 'poll.closed', defaultMessage: 'Closed' },
|
closed: { id: 'poll.closed', defaultMessage: 'Closed' },
|
||||||
|
@ -103,6 +104,7 @@ class Poll extends ImmutablePureComponent {
|
||||||
const percent = poll.get('votes_count') === 0 ? 0 : (option.get('votes_count') / poll.get('votes_count')) * 100;
|
const percent = poll.get('votes_count') === 0 ? 0 : (option.get('votes_count') / poll.get('votes_count')) * 100;
|
||||||
const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') > other.get('votes_count'));
|
const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') > other.get('votes_count'));
|
||||||
const active = !!this.state.selected[`${optionIndex}`];
|
const active = !!this.state.selected[`${optionIndex}`];
|
||||||
|
const voted = option.get('voted') || (poll.get('own_votes') && poll.get('own_votes').includes(optionIndex));
|
||||||
|
|
||||||
let titleEmojified = option.get('title_emojified');
|
let titleEmojified = option.get('title_emojified');
|
||||||
if (!titleEmojified) {
|
if (!titleEmojified) {
|
||||||
|
@ -131,7 +133,10 @@ class Poll extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!showResults && <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} />}
|
{!showResults && <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} />}
|
||||||
{showResults && <span className='poll__number'>{Math.round(percent)}%</span>}
|
{showResults && <span className='poll__number'>
|
||||||
|
{!!voted && <Icon id='check' className='poll__vote__mark' />}
|
||||||
|
{Math.round(percent)}%
|
||||||
|
</span>}
|
||||||
|
|
||||||
<span dangerouslySetInnerHTML={{ __html: titleEmojified }} />
|
<span dangerouslySetInnerHTML={{ __html: titleEmojified }} />
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -95,13 +95,19 @@
|
||||||
|
|
||||||
&__number {
|
&__number {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 36px;
|
width: 48px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
flex: 0 0 36px;
|
flex: 0 0 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__vote__mark {
|
||||||
|
float: left;
|
||||||
|
color: $valid-value-color;
|
||||||
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
|
|
|
@ -54,6 +54,10 @@ class Poll < ApplicationRecord
|
||||||
account.id == account_id || votes.where(account: account).exists?
|
account.id == account_id || votes.where(account: account).exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def own_votes(account)
|
||||||
|
votes.where(account: account).pluck(:choice)
|
||||||
|
end
|
||||||
|
|
||||||
delegate :local?, to: :account
|
delegate :local?, to: :account
|
||||||
|
|
||||||
def remote?
|
def remote?
|
||||||
|
|
|
@ -8,6 +8,7 @@ class REST::PollSerializer < ActiveModel::Serializer
|
||||||
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
||||||
|
|
||||||
attribute :voted, if: :current_user?
|
attribute :voted, if: :current_user?
|
||||||
|
attribute :own_votes, if: :current_user?
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id.to_s
|
object.id.to_s
|
||||||
|
@ -21,6 +22,10 @@ class REST::PollSerializer < ActiveModel::Serializer
|
||||||
object.voted?(current_user.account)
|
object.voted?(current_user.account)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def own_votes
|
||||||
|
object.own_votes(current_user.account)
|
||||||
|
end
|
||||||
|
|
||||||
def current_user?
|
def current_user?
|
||||||
!current_user.nil?
|
!current_user.nil?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
- show_results = (user_signed_in? && poll.voted?(current_account)) || poll.expired?
|
- show_results = (user_signed_in? && poll.voted?(current_account)) || poll.expired?
|
||||||
|
- own_votes = user_signed_in? ? poll.own_votes(current_account) : []
|
||||||
|
|
||||||
.poll
|
.poll
|
||||||
%ul
|
%ul
|
||||||
- poll.loaded_options.each do |option|
|
- poll.loaded_options.each_with_index do |option, index|
|
||||||
%li
|
%li
|
||||||
- if show_results
|
- if show_results
|
||||||
- percent = poll.votes_count > 0 ? 100 * option.votes_count / poll.votes_count : 0
|
- percent = poll.votes_count > 0 ? 100 * option.votes_count / poll.votes_count : 0
|
||||||
%span.poll__chart{ style: "width: #{percent}%" }
|
%span.poll__chart{ style: "width: #{percent}%" }
|
||||||
|
|
||||||
%label.poll__text><
|
%label.poll__text><
|
||||||
%span.poll__number= percent.round
|
%span.poll__number><
|
||||||
|
- if own_votes.include?(index)
|
||||||
|
%i.poll__vote__mark.fa.fa-check
|
||||||
|
= percent.round
|
||||||
= Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
|
= Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
|
||||||
- else
|
- else
|
||||||
%label.poll__text><
|
%label.poll__text><
|
||||||
|
|
Loading…
Reference in New Issue