about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-03-20 17:29:12 +0100
committerEugen Rochko <eugen@zeonfederated.com>2019-03-20 17:29:12 +0100
commit80f0910e2141b24082b9143266a9a6cf1ef6a516 (patch)
treeaf7a96def3f54cdb4b8fcb11adc1ecb36166c529
parent66d945209278c9344d503fe4e7a58d5c6f040e50 (diff)
Add support for custom emojis in poll options (#10322)
* Backend changes for custom emoji support in poll options

* Serialize poll emojis in REST API

* Render custom emojis in poll options

* Render custom emoji in poll options on public pages
-rw-r--r--app/javascript/mastodon/actions/importer/normalizer.js4
-rw-r--r--app/javascript/mastodon/components/poll.js13
-rw-r--r--app/lib/formatter.rb6
-rw-r--r--app/models/poll.rb4
-rw-r--r--app/models/status.rb6
-rw-r--r--app/serializers/rest/poll_serializer.rb1
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml2
-rw-r--r--app/views/stream_entries/_poll.html.haml4
-rw-r--r--app/views/stream_entries/_simple_status.html.haml2
9 files changed, 35 insertions, 7 deletions
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index ea80c0efb..5badb0c49 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -71,9 +71,11 @@ export function normalizeStatus(status, normalOldStatus) {
 export function normalizePoll(poll) {
   const normalPoll = { ...poll };
 
+  const emojiMap = makeEmojiMap(normalPoll);
+
   normalPoll.options = poll.options.map(option => ({
     ...option,
-    title_emojified: emojify(escapeTextContentForBrowser(option.title)),
+    title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
   }));
 
   return normalPoll;
diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js
index a1b297ce7..56331cb29 100644
--- a/app/javascript/mastodon/components/poll.js
+++ b/app/javascript/mastodon/components/poll.js
@@ -44,6 +44,11 @@ const timeRemainingString = (intl, date, now) => {
   return relativeTime;
 };
 
+const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
+  obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
+  return obj;
+}, {});
+
 export default @injectIntl
 class Poll extends ImmutablePureComponent {
 
@@ -99,6 +104,12 @@ class Poll extends ImmutablePureComponent {
     const active             = !!this.state.selected[`${optionIndex}`];
     const showResults        = poll.get('voted') || poll.get('expired');
 
+    let titleEmojified = option.get('title_emojified');
+    if (!titleEmojified) {
+      const emojiMap = makeEmojiMap(poll);
+      titleEmojified = emojify(escapeTextContentForBrowser(option.get('title')), emojiMap);
+    }
+
     return (
       <li key={option.get('title')}>
         {showResults && (
@@ -122,7 +133,7 @@ class Poll extends ImmutablePureComponent {
           {!showResults && <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} />}
           {showResults && <span className='poll__number'>{Math.round(percent)}%</span>}
 
-          <span dangerouslySetInnerHTML={{ __html: option.get('title_emojified', emojify(escapeTextContentForBrowser(option.get('title')))) }} />
+          <span dangerouslySetInnerHTML={{ __html: titleEmojified }} />
         </label>
       </li>
     );
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 464e1ee7e..aadf03b2a 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -71,6 +71,12 @@ class Formatter
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
+  def format_poll_option(status, option, **options)
+    html = encode(option.title)
+    html = encode_custom_emojis(html, status.emojis, options[:autoplay])
+    html.html_safe # rubocop:disable Rails/OutputSafety
+  end
+
   def format_display_name(account, **options)
     html = encode(account.display_name.presence || account.username)
     html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
diff --git a/app/models/poll.rb b/app/models/poll.rb
index 6df230337..8f72c7b11 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -60,6 +60,10 @@ class Poll < ApplicationRecord
     !local?
   end
 
+  def emojis
+    @emojis ||= CustomEmoji.from_text(options.join(' '), account.domain)
+  end
+
   class Option < ActiveModelSerializers::Model
     attributes :id, :title, :votes_count, :poll
 
diff --git a/app/models/status.rb b/app/models/status.rb
index b9479c76b..d3fb83cca 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -213,7 +213,11 @@ class Status < ApplicationRecord
   end
 
   def emojis
-    @emojis ||= CustomEmoji.from_text([spoiler_text, text].join(' '), account.domain)
+    return @emojis if defined?(@emojis)
+    fields = [spoiler_text, text]
+    fields += owned_poll.options unless owned_poll.nil?
+    @emojis = CustomEmoji.from_text(fields.join(' '), account.domain)
+    @emojis
   end
 
   def mark_for_mass_destruction!
diff --git a/app/serializers/rest/poll_serializer.rb b/app/serializers/rest/poll_serializer.rb
index 4dae1c09f..356c45b83 100644
--- a/app/serializers/rest/poll_serializer.rb
+++ b/app/serializers/rest/poll_serializer.rb
@@ -5,6 +5,7 @@ class REST::PollSerializer < ActiveModel::Serializer
              :multiple, :votes_count
 
   has_many :loaded_options, key: :options
+  has_many :emojis, serializer: REST::CustomEmojiSerializer
 
   attribute :voted, if: :current_user?
 
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index b19d2452a..d18ecd37a 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -24,7 +24,7 @@
 
   - 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 do
-      = render partial: 'stream_entries/poll', locals: { poll: status.poll }
+      = render partial: 'stream_entries/poll', locals: { status: status, poll: status.poll, autoplay: autoplay }
   - 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
index d6b2c0cd9..ba34890df 100644
--- a/app/views/stream_entries/_poll.html.haml
+++ b/app/views/stream_entries/_poll.html.haml
@@ -10,11 +10,11 @@
 
           %label.poll__text><
             %span.poll__number= percent.round
-            = option.title
+            = Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
         - else
           %label.poll__text><
             %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
-            = option.title
+            = Formatter.instance.format_poll_option(status, option, autoplay: autoplay)
   .poll__footer
     - unless show_results
       %button.button.button-secondary{ disabled: true }
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 68e48edbb..a499a8634 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -28,7 +28,7 @@
 
   - 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 do
-      = render partial: 'stream_entries/poll', locals: { poll: status.poll }
+      = render partial: 'stream_entries/poll', locals: { status: status, poll: status.poll, autoplay: autoplay }
   - elsif !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first