Show a file in a message's rich text without making its preview on view

Files are posted as a message's own attachment, and the composer puts none in the rich text: it permits
only mentions and link embeds. The server still keeps any attachment whose signed id resolves, a blob
included, and Action Text's blob partial links to a representation URL that makes the preview on the
first request, without the limits the POST has, and on every request while it fails.

The app's own partial shows an image's preview only if it was already made, and otherwise the file's
name and size, as it does for a file that isn't an image. The cached presentation's version goes up,
since Action Text renders the partial by name and the template digest doesn't see it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bj8KnxpTf9sj2Ysa8aLAVa
This commit is contained in:
Marcello Costagliola
2026-10-06 15:40:27 +02:00
parent 32b4144b52
commit 15f56134e7
3 changed files with 52 additions and 1 deletions
@@ -0,0 +1,20 @@
<%# Files are posted as a message's own attachment, which gets its preview when it's posted; the composer adds none to
the rich text. One that's there anyway shows a preview only if it was already made, rather than linking to a URL
that makes it, on any view, for as long as it fails. %>
<% preview = blob.variant(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) if blob.variable? %>
<% preview = nil unless preview&.processed? %>
<figure class="attachment attachment--<%= preview ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
<% if preview %>
<%= image_tag preview, alt: blob.try(:alt) %>
<% end %>
<figcaption class="attachment__caption">
<% if caption = blob.try(:caption) %>
<%= caption %>
<% else %>
<span class="attachment__name"><%= blob.filename %></span>
<span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
<% end %>
</figcaption>
</figure>
+1 -1
View File
@@ -1,7 +1,7 @@
<%# Be sure to check/update messages/_template.html.erb when changing this file %>
<%# Bump this version when the message presentation filters change what they emit. Editing this line changes the template digest, which busts BOTH this fragment cache and the collection cache that keys on this partial's digest (helper Ruby changes alone don't). %>
<% cache [ message, "presentation-v3" ] do %>
<% cache [ message, "presentation-v4" ] do %>
<%= message_tag message do %>
<h2 class="message__day-separator"><%= local_datetime_tag message.created_at, style: :date %></h2>
+31
View File
@@ -24,4 +24,35 @@ class MessagesHelperTest < ActionView::TestCase
assert_match /<a href="https:\/\/example\.com"[^>]*>example<\/a>/, presentation
assert_match /<strong>bold<\/strong>/, presentation
end
test "message_presentation shows an image in the rich text as a file, rather than making its preview on view" do
presentation = view.message_presentation(message_with_file_in_rich_text("moon.jpg", "image/jpeg"))
assert_no_match %r{/representations/}, presentation
assert_match %r{<span class="attachment__name">moon\.jpg</span>}, presentation
end
test "message_presentation shows an image in the rich text whose preview was already made" do
message = message_with_file_in_rich_text("moon.jpg", "image/jpeg")
message.body.embeds.first.blob.variant(resize_to_limit: [ 1024, 768 ]).processed
presentation = view.message_presentation(message.reload)
assert_match %r{<img[^>]+src="[^"]*/representations/[^"]*moon\.jpg"}, presentation
end
test "message_presentation shows a video in the rich text as a file" do
presentation = view.message_presentation(message_with_file_in_rich_text("alpha-centuri.mov", "video/quicktime"))
assert_no_match %r{/representations/}, presentation
assert_match %r{<span class="attachment__name">alpha-centuri\.mov</span>}, presentation
end
private
def message_with_file_in_rich_text(file, content_type)
blob = ActiveStorage::Blob.create_and_upload!(io: file_fixture(file).open, filename: file, content_type: content_type)
body = %(<div>Here: <action-text-attachment sgid="#{blob.attachable_sgid}"></action-text-attachment></div>)
Message.create! room: rooms(:pets), body: body, client_message_id: "0015", creator: users(:jason)
end
end