From 230a012f0090c496fc5cdb011bcc8ed732fd0f5c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 3 Mar 2019 22:18:23 +0100 Subject: Add polls (#10111) * Add polls Fix #1629 * Add tests * Fixes * Change API for creating polls * Use name instead of content for votes * Remove poll validation for remote polls * Add polls to public pages * When updating the poll, update options just in case they were changed * Fix public pages showing both poll and other media --- app/javascript/styles/application.scss | 1 + app/javascript/styles/mastodon/components.scss | 4 ++ app/javascript/styles/mastodon/polls.scss | 95 ++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 app/javascript/styles/mastodon/polls.scss (limited to 'app/javascript/styles') diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index 4bce74187..6db3bc3dc 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -16,6 +16,7 @@ @import 'mastodon/stream_entries'; @import 'mastodon/boost'; @import 'mastodon/components'; +@import 'mastodon/polls'; @import 'mastodon/introduction'; @import 'mastodon/modal'; @import 'mastodon/emoji_picker'; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 0163e90b9..ceb28dd19 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -105,6 +105,10 @@ border-color: lighten($ui-primary-color, 4%); color: lighten($darker-text-color, 4%); } + + &:disabled { + opacity: 0.5; + } } &.button--block { diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss new file mode 100644 index 000000000..b93d36e92 --- /dev/null +++ b/app/javascript/styles/mastodon/polls.scss @@ -0,0 +1,95 @@ +.poll { + margin-top: 16px; + font-size: 14px; + + li { + margin-bottom: 10px; + position: relative; + } + + &__chart { + position: absolute; + top: 0; + left: 0; + height: 100%; + display: inline-block; + border-radius: 4px; + background: darken($ui-primary-color, 14%); + + &.leading { + background: $ui-highlight-color; + } + } + + &__text { + position: relative; + display: inline-block; + padding: 6px 0; + line-height: 18px; + cursor: default; + + input[type=radio], + input[type=checkbox] { + display: none; + } + + &.selectable { + cursor: pointer; + } + } + + &__input { + display: inline-block; + position: relative; + border: 1px solid $ui-primary-color; + box-sizing: border-box; + width: 18px; + height: 18px; + margin-right: 10px; + top: -1px; + border-radius: 4px; + vertical-align: middle; + + &.active { + border-color: $valid-value-color; + background: $valid-value-color; + } + } + + &__number { + display: inline-block; + width: 36px; + font-weight: 700; + padding: 0 10px; + text-align: right; + } + + &__footer { + padding-top: 6px; + padding-bottom: 5px; + color: $dark-text-color; + } + + &__link { + display: inline; + background: transparent; + padding: 0; + margin: 0; + border: 0; + color: $dark-text-color; + text-decoration: underline; + + &:hover, + &:focus, + &:active { + text-decoration: none; + } + } + + .button { + height: 36px; + padding: 0 16px; + margin-right: 10px; + font-size: 14px; + } +} -- cgit From 4ced609497bc736cb2b1aec921ba5ca7a23a7f53 Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 4 Mar 2019 01:54:14 +0100 Subject: Fixes to the polls UI (#10150) * Allow unselecting choices in multiple choice polls * Properly disable checkboxes/radio buttons for polls in public pages * Visually differentiate checkboxes and radio buttons --- app/javascript/mastodon/components/poll.js | 19 ++++++++++++------- app/javascript/styles/mastodon/polls.scss | 6 +++++- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'app/javascript/styles') diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js index c18ee1505..45ce107aa 100644 --- a/app/javascript/mastodon/components/poll.js +++ b/app/javascript/mastodon/components/poll.js @@ -60,7 +60,11 @@ class Poll extends ImmutablePureComponent { if (this.props.poll.get('multiple')) { const tmp = { ...this.state.selected }; - tmp[value] = true; + if (tmp[value]) { + delete tmp[value]; + } else { + tmp[value] = true; + } this.setState({ selected: tmp }); } else { const tmp = {}; @@ -86,11 +90,11 @@ class Poll extends ImmutablePureComponent { }; renderOption (option, optionIndex) { - const { poll } = this.props; - const percent = (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 active = !!this.state.selected[`${optionIndex}`]; - const showResults = poll.get('voted') || poll.get('expired'); + const { poll, disabled } = this.props; + const percent = (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 active = !!this.state.selected[`${optionIndex}`]; + const showResults = poll.get('voted') || poll.get('expired'); return (
  • @@ -109,9 +113,10 @@ class Poll extends ImmutablePureComponent { value={optionIndex} checked={active} onChange={this.handleOptionChange} + disabled={disabled} /> - {!showResults && } + {!showResults && } {showResults && {Math.floor(percent)}%} {option.get('title')} diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index b93d36e92..f42496559 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -47,9 +47,13 @@ height: 18px; margin-right: 10px; top: -1px; - border-radius: 4px; + border-radius: 50%; vertical-align: middle; + &.checkbox { + border-radius: 4px; + } + &.active { border-color: $valid-value-color; background: $valid-value-color; -- cgit From a198add83bb527c32fa0e01404338562b157da99 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 5 Mar 2019 03:51:18 +0100 Subject: Fix various issues in polls (#10165) * Fix ActivityPub poll results being serialized even with hide_totals * Fix poll refresh button having a different font size * Display poll in OpenGraph description * Fix NoMethodError when serializing votes Regression from #10158 * Fix polls on public pages being broken for non-logged-in users * Do not show time remaining if poll has no expiration date --- app/helpers/stream_entries_helper.rb | 12 +++++++++++- app/javascript/mastodon/components/poll.js | 5 +++-- app/javascript/styles/mastodon/polls.scss | 1 + app/models/poll_vote.rb | 4 ++++ app/serializers/activitypub/note_serializer.rb | 12 ++++++++---- app/views/stream_entries/_poll.html.haml | 16 ++++++++++------ 6 files changed, 37 insertions(+), 13 deletions(-) (limited to 'app/javascript/styles') diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 7a74c0b7d..8392afa73 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -104,9 +104,19 @@ module StreamEntriesHelper I18n.t('statuses.content_warning', warning: status.spoiler_text) end + def poll_summary(status) + return unless status.poll + status.poll.options.map { |o| "[ ] #{o}" }.join("\n") + end + def status_description(status) components = [[media_summary(status), status_text_summary(status)].reject(&:blank?).join(' · ')] - components << status.text if status.spoiler_text.blank? + + if status.spoiler_text.blank? + components << status.text + components << poll_summary(status) + end + components.reject(&:blank?).join("\n\n") end diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js index e9124aefa..182491af8 100644 --- a/app/javascript/mastodon/components/poll.js +++ b/app/javascript/mastodon/components/poll.js @@ -118,7 +118,7 @@ class Poll extends ImmutablePureComponent { /> {!showResults && } - {showResults && {Math.floor(percent)}%} + {showResults && {Math.round(percent)}%} {option.get('title')} @@ -146,7 +146,8 @@ class Poll extends ImmutablePureComponent {
    {!showResults && } {showResults && !this.props.disabled && · } - · {timeRemaining} + + {poll.get('expires_at') && · {timeRemaining}}
    ); diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index f42496559..7c6e61d63 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -82,6 +82,7 @@ border: 0; color: $dark-text-color; text-decoration: underline; + font-size: inherit; &:hover, &:focus, diff --git a/app/models/poll_vote.rb b/app/models/poll_vote.rb index aec678968..9ad66bbf8 100644 --- a/app/models/poll_vote.rb +++ b/app/models/poll_vote.rb @@ -23,6 +23,10 @@ class PollVote < ApplicationRecord delegate :local?, to: :account + def object_type + :vote + end + private def increment_counter_cache diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index b2c92fdc1..b2a5f53e0 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -15,8 +15,8 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer has_one :replies, serializer: ActivityPub::CollectionSerializer, if: :local? - has_many :poll_loaded_options, key: :one_of, if: :poll_and_not_multiple? - has_many :poll_loaded_options, key: :any_of, if: :poll_and_multiple? + has_many :poll_options, key: :one_of, if: :poll_and_not_multiple? + has_many :poll_options, key: :any_of, if: :poll_and_multiple? attribute :end_time, if: :poll_and_expires? attribute :closed, if: :poll_and_expired? @@ -121,8 +121,12 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer object.account.local? end - def poll_loaded_options - object.poll.loaded_options + def poll_options + if !object.expired? && object.hide_totals? + object.poll.unloaded_options + else + object.poll.loaded_options + end end def poll_and_multiple? diff --git a/app/views/stream_entries/_poll.html.haml b/app/views/stream_entries/_poll.html.haml index 974aff9bd..c7e5e0c63 100644 --- a/app/views/stream_entries/_poll.html.haml +++ b/app/views/stream_entries/_poll.html.haml @@ -1,5 +1,5 @@ -- options = (!poll.expired? && poll.hide_totals?) ? poll.unloaded_options : poll.loaded_options -- voted = poll.votes.where(account: current_user.account).exists? +- options = (!poll.expired? && poll.hide_totals?) ? poll.unloaded_options : poll.loaded_options +- voted = user_signed_in? && poll.votes.where(account: current_account).exists? - show_results = voted || poll.expired? .poll @@ -9,17 +9,21 @@ - if show_results - percent = 100 * option.votes_count / poll.votes_count %span.poll__chart{ style: "width: #{percent}%" } + %label.poll__text>< - %span.poll__number= percent + %span.poll__number= percent.round = option.title - else %label.poll__text>< - %span.poll__input{ class: poll.multiple ? 'checkbox' : nil}>< + %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< = option.title .poll__footer - unless show_results %button.button.button-secondary{ disabled: true } = t('statuses.poll.vote') + %span= t('statuses.poll.total_votes', count: poll.votes_count) - · - %span= poll.expires_at + + - unless poll.expires_at.nil? + · + %span= l poll.expires_at -- cgit