Change hashtags at the end of the post to render out-of-band

This commit is contained in:
Eugen Rochko 2023-08-14 20:19:05 +02:00 committed by Claire
parent 26eaf058e2
commit a16299b65a
3 changed files with 75 additions and 5 deletions

View file

@ -15,7 +15,7 @@ module FormattingHelper
module_function :extract_status_plain_text module_function :extract_status_plain_text
def status_content_format(status) def status_content_format(status)
html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : [])) html_aware_format(status.text, status.local?, strip_rich_entities: true, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
end end
def rss_status_content_format(status) def rss_status_content_format(status)

View file

@ -20,6 +20,7 @@ class TextFormatter
# @option options [Boolean] :multiline # @option options [Boolean] :multiline
# @option options [Boolean] :with_domains # @option options [Boolean] :with_domains
# @option options [Boolean] :with_rel_me # @option options [Boolean] :with_rel_me
# @option options [Boolean] :strip_rich_entities
# @option options [Array<Account>] :preloaded_accounts # @option options [Array<Account>] :preloaded_accounts
def initialize(text, options = {}) def initialize(text, options = {})
@text = text @text = text
@ -75,16 +76,22 @@ class TextFormatter
entity[:indices].first entity[:indices].first
end end
interrupt_text_at = detect_hashtag_block if options[:strip_rich_entities]
result = +'' result = +''
last_index = entities.reduce(0) do |index, entity| last_index = entities.reduce(0) do |index, entity|
indices = entity[:indices] indices = entity[:indices]
result << h(text[index...indices.first]) result << h(text[index...indices.first])
break interrupt_text_at if interrupt_text_at.present? && indices.first >= interrupt_text_at
result << yield(entity) result << yield(entity)
indices.last indices.last
end end
result << h(text[last_index..]) result << h(text[last_index..]) unless interrupt_text_at.present? && last_index >= interrupt_text_at
result result
end end
@ -163,4 +170,38 @@ class TextFormatter
def preloaded_accounts? def preloaded_accounts?
preloaded_accounts.present? preloaded_accounts.present?
end end
def detect_hashtag_block
block_begin = nil
block_end = nil
entities.map.with_index do |entity, i|
next unless entity[:hashtag]
next_entity = entities[i + 1]
if !next_entity.nil? && !next_entity[:hashtag]
block_begin = nil
block_end = nil
next
elsif next_entity.nil?
block_begin = entity[:indices].first if block_begin.nil?
block_end = entity[:indices].last
next
end
entity_end = entity[:indices].last
next_entity_start = next_entity[:indices].first
if next_entity_start == entity_end + 1
block_begin = entity[:indices].first if block_begin.nil?
block_end = entity_end
else
block_begin = nil
block_end = nil
end
end
block_begin if block_begin.present? && block_end.present? && text[block_end..].blank? && text[block_begin - 1] == "\n"
end
end end

View file

@ -4,8 +4,9 @@ require 'rails_helper'
RSpec.describe TextFormatter do RSpec.describe TextFormatter do
describe '#to_s' do describe '#to_s' do
subject { described_class.new(text, preloaded_accounts: preloaded_accounts).to_s } subject { described_class.new(text, strip_rich_entities: strip_rich_entities, preloaded_accounts: preloaded_accounts).to_s }
let(:strip_rich_entities) { false }
let(:preloaded_accounts) { nil } let(:preloaded_accounts) { nil }
context 'when given text containing plain text' do context 'when given text containing plain text' do
@ -273,7 +274,7 @@ RSpec.describe TextFormatter do
end end
context 'when given text containing a hashtag' do context 'when given text containing a hashtag' do
let(:text) { '#hashtag' } let(:text) { 'foo #hashtag' }
it 'creates a hashtag link' do it 'creates a hashtag link' do
expect(subject).to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>' expect(subject).to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
@ -281,7 +282,7 @@ RSpec.describe TextFormatter do
end end
context 'when given text containing a hashtag with Unicode chars' do context 'when given text containing a hashtag with Unicode chars' do
let(:text) { '#hashtagタグ' } let(:text) { 'foo #hashtagタグ' }
it 'creates a hashtag link' do it 'creates a hashtag link' do
expect(subject).to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>' expect(subject).to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
@ -311,5 +312,33 @@ RSpec.describe TextFormatter do
expect(subject).to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"' expect(subject).to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
end end
end end
context 'with strip_rich_entities option' do
let(:strip_rich_entities) { true }
context 'when a hashtag is in the middle of a sentence' do
let(:text) { 'Hello #foo world' }
it 'keeps the hashtag in place' do
expect(subject).to include 'foo'
end
end
context 'when a hashtag starts a line but is followed by text' do
let(:text) { '#foo Hello world' }
it 'keeps the hashtag in place' do
expect(subject).to include 'foo'
end
end
context 'when a hashtag is on the last line in the text' do
let(:text) { "Hello world\n#foo" }
it 'strips out the hashtag' do
expect(subject).to_not include 'foo'
end
end
end
end end
end end