Skip to content

Commit

Permalink
Add support for custom emojis in poll options (#10322)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ClearlyClaire authored and Gargron committed Mar 20, 2019
1 parent 66d9452 commit 80f0910
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 7 deletions.
4 changes: 3 additions & 1 deletion app/javascript/mastodon/actions/importer/normalizer.js
Expand Up @@ -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;
Expand Down
13 changes: 12 additions & 1 deletion app/javascript/mastodon/components/poll.js
Expand Up @@ -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 {

Expand Down Expand Up @@ -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 && (
Expand All @@ -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>
);
Expand Down
6 changes: 6 additions & 0 deletions app/lib/formatter.rb
Expand Up @@ -71,6 +71,12 @@ def format_spoiler(status, **options)
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]
Expand Down
4 changes: 4 additions & 0 deletions app/models/poll.rb
Expand Up @@ -60,6 +60,10 @@ def remote?
!local?
end

def emojis
@emojis ||= CustomEmoji.from_text(options.join(' '), account.domain)
end

class Option < ActiveModelSerializers::Model
attributes :id, :title, :votes_count, :poll

Expand Down
6 changes: 5 additions & 1 deletion app/models/status.rb
Expand Up @@ -213,7 +213,11 @@ def non_sensitive_with_media?
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!
Expand Down
1 change: 1 addition & 0 deletions app/serializers/rest/poll_serializer.rb
Expand Up @@ -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?

Expand Down
2 changes: 1 addition & 1 deletion app/views/stream_entries/_detailed_status.html.haml
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions app/views/stream_entries/_poll.html.haml
Expand Up @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion app/views/stream_entries/_simple_status.html.haml
Expand Up @@ -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
Expand Down

0 comments on commit 80f0910

Please sign in to comment.