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/views/stream_entries/_detailed_status.html.haml | 4 +++- app/views/stream_entries/_simple_status.html.haml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index e123d657f..b9327a546 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -22,7 +22,9 @@ %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more') .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - - if !status.media_attachments.empty? + - if status.poll + = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json + - elsif !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index b0b6e80c8..a000c02f4 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -26,7 +26,9 @@ %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more') .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - - if !status.media_attachments.empty? + - if status.poll + = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json + - elsif !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do -- cgit From 3de71887d849103ed62e8b04b54c630763881010 Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 4 Mar 2019 18:03:12 +0100 Subject: Add non-JS fallback for polls on public pages (#10155) --- .../stream_entries/_detailed_status.html.haml | 3 ++- app/views/stream_entries/_poll.html.haml | 25 ++++++++++++++++++++++ app/views/stream_entries/_simple_status.html.haml | 3 ++- config/locales/en.yml | 5 +++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 app/views/stream_entries/_poll.html.haml (limited to 'app/views') diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index b9327a546..b19d2452a 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -23,7 +23,8 @@ .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.poll - = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json + = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do + = render partial: 'stream_entries/poll', locals: { poll: status.poll } - elsif !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first diff --git a/app/views/stream_entries/_poll.html.haml b/app/views/stream_entries/_poll.html.haml new file mode 100644 index 000000000..974aff9bd --- /dev/null +++ b/app/views/stream_entries/_poll.html.haml @@ -0,0 +1,25 @@ +- options = (!poll.expired? && poll.hide_totals?) ? poll.unloaded_options : poll.loaded_options +- voted = poll.votes.where(account: current_user.account).exists? +- show_results = voted || poll.expired? + +.poll + %ul + - options.each do |option| + %li + - if show_results + - percent = 100 * option.votes_count / poll.votes_count + %span.poll__chart{ style: "width: #{percent}%" } + %label.poll__text>< + %span.poll__number= percent + = option.title + - else + %label.poll__text>< + %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 diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index a000c02f4..68e48edbb 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -27,7 +27,8 @@ .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay) - if status.poll - = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json + = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do + = render partial: 'stream_entries/poll', locals: { poll: status.poll } - elsif !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first diff --git a/config/locales/en.yml b/config/locales/en.yml index b1f6166b4..2d23b1eb6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -853,6 +853,11 @@ en: ownership: Someone else's toot cannot be pinned private: Non-public toot cannot be pinned reblog: A boost cannot be pinned + poll: + total_votes: + one: "%{count} vote" + other: "%{count} votes" + vote: Vote show_more: Show more sign_in_to_participate: Sign in to participate in the conversation title: '%{name}: "%{quote}"' -- 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/views') 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 From f44091d6535463e08a90dbf3d108540bf587cec1 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Thu, 7 Mar 2019 09:49:42 +0900 Subject: Fix #10202 (#10203) --- app/views/stream_entries/_poll.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/views') diff --git a/app/views/stream_entries/_poll.html.haml b/app/views/stream_entries/_poll.html.haml index c7e5e0c63..dad04b79c 100644 --- a/app/views/stream_entries/_poll.html.haml +++ b/app/views/stream_entries/_poll.html.haml @@ -7,7 +7,7 @@ - options.each do |option| %li - if show_results - - percent = 100 * option.votes_count / poll.votes_count + - percent = poll.votes_count > 0 ? 100 * option.votes_count / poll.votes_count : 0 %span.poll__chart{ style: "width: #{percent}%" } %label.poll__text>< -- cgit From 054bbb3da21b2c76374eb921cba862adb8d5a0b3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 7 Mar 2019 22:53:47 +0100 Subject: Immediately display poll results to poll author (#10187) * Immediately display poll results to poll author * Refactor Poll#loaded_options and add Poll#voted? to improve DRYness --- app/models/poll.rb | 14 +++++++++----- app/serializers/activitypub/note_serializer.rb | 6 +----- app/serializers/rest/poll_serializer.rb | 12 ++---------- app/views/stream_entries/_poll.html.haml | 6 ++---- 4 files changed, 14 insertions(+), 24 deletions(-) (limited to 'app/views') diff --git a/app/models/poll.rb b/app/models/poll.rb index da2e25e71..14a38026a 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -41,17 +41,17 @@ class Poll < ApplicationRecord after_commit :reset_parent_cache, on: :update def loaded_options - options.map.with_index { |title, key| Option.new(self, key.to_s, title, cached_tallies[key]) } - end - - def unloaded_options - options.map.with_index { |title, key| Option.new(self, key.to_s, title, nil) } + options.map.with_index { |title, key| Option.new(self, key.to_s, title, show_totals_now? ? cached_tallies[key] : nil) } end def possibly_stale? remote? && last_fetched_before_expiration? && time_passed_since_last_fetch? end + def voted?(account) + account.id == account_id || votes.where(account: account).exists? + end + delegate :local?, to: :account def remote? @@ -95,4 +95,8 @@ class Poll < ApplicationRecord def time_passed_since_last_fetch? last_fetched_at.nil? || last_fetched_at < 1.minute.ago end + + def show_totals_now? + expired? || !hide_totals? + end end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index 3a9e388a5..553f333d8 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -122,11 +122,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer end def poll_options - if !object.poll.expired? && object.poll.hide_totals? - object.poll.unloaded_options - else - object.poll.loaded_options - end + object.poll.loaded_options end def poll_and_multiple? diff --git a/app/serializers/rest/poll_serializer.rb b/app/serializers/rest/poll_serializer.rb index b02e8ca93..4dae1c09f 100644 --- a/app/serializers/rest/poll_serializer.rb +++ b/app/serializers/rest/poll_serializer.rb @@ -4,7 +4,7 @@ class REST::PollSerializer < ActiveModel::Serializer attributes :id, :expires_at, :expired, :multiple, :votes_count - has_many :dynamic_options, key: :options + has_many :loaded_options, key: :options attribute :voted, if: :current_user? @@ -12,20 +12,12 @@ class REST::PollSerializer < ActiveModel::Serializer object.id.to_s end - def dynamic_options - if !object.expired? && object.hide_totals? - object.unloaded_options - else - object.loaded_options - end - end - def expired object.expired? end def voted - object.votes.where(account: current_user.account).exists? + object.voted?(current_user.account) end def current_user? diff --git a/app/views/stream_entries/_poll.html.haml b/app/views/stream_entries/_poll.html.haml index dad04b79c..d6b2c0cd9 100644 --- a/app/views/stream_entries/_poll.html.haml +++ b/app/views/stream_entries/_poll.html.haml @@ -1,10 +1,8 @@ -- 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? +- show_results = (user_signed_in? && poll.voted?(current_account)) || poll.expired? .poll %ul - - options.each do |option| + - poll.loaded_options.each do |option| %li - if show_results - percent = poll.votes_count > 0 ? 100 * option.votes_count / poll.votes_count : 0 -- cgit From 6c0a96912a855561d0bbe757334310bbe4d8d5c8 Mon Sep 17 00:00:00 2001 From: Aditoo17 <42938951+Aditoo17@users.noreply.github.com> Date: Sat, 9 Mar 2019 17:07:59 +0100 Subject: Change joinmastodon.org to joinmastodon.org/#getting-started for sign up on another server (#10224) --- app/views/about/_forms.html.haml | 2 +- app/views/about/_links.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app/views') diff --git a/app/views/about/_forms.html.haml b/app/views/about/_forms.html.haml index 81f7173f7..78a422690 100644 --- a/app/views/about/_forms.html.haml +++ b/app/views/about/_forms.html.haml @@ -1,7 +1,7 @@ - if @instance_presenter.open_registrations = render 'registration' - else - = link_to t('auth.register_elsewhere'), 'https://joinmastodon.org', class: 'button button-primary' + = link_to t('auth.register_elsewhere'), 'https://joinmastodon.org/#getting-started', class: 'button button-primary' .closed-registrations-message - if @instance_presenter.closed_registrations_message.blank? diff --git a/app/views/about/_links.html.haml b/app/views/about/_links.html.haml index f79c37e65..381f301f9 100644 --- a/app/views/about/_links.html.haml +++ b/app/views/about/_links.html.haml @@ -11,6 +11,6 @@ = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn' %li= link_to t('about.about_this'), about_more_path %li - = link_to 'https://joinmastodon.org/' do + = link_to 'https://joinmastodon.org/#getting-started' do = "#{t('about.other_instances')}" %i.fa.fa-external-link{ style: 'padding-left: 5px;' } -- cgit