Skip to content

Commit

Permalink
FEATURE: Add thumbnails for chat image uploads (discourse#24328)
Browse files Browse the repository at this point in the history
Introduces the concept of image thumbnails in chat, prior to this we uploaded and used full size chat images within channels and direct messages.

The following changes are covered:
- Post processing of image uploads to create the thumbnail within Chat::MessageProcessor
- Extract responsive image ratios into CookedProcessorMixin (used for creating upload variations)
- Add thumbnail to upload serializer from plugin.rb
- Convert chat upload template to glimmer component using .gjs format
- Use thumbnail image within chat upload component (stores full size img in orig-src data attribute)
- Old uploads which don't have thumbnails will fallback to full size images in channels/DMs
- Update Magnific lightbox to use full size image when clicked
- Update Glimmer lightbox to use full size image (enables zooming for chat images)
  • Loading branch information
dbattersby authored Dec 6, 2023
1 parent 30d5e75 commit 8b46dc8
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export async function processHTML({ container, selector, clickTarget }) {
item.parentElement?.style?.backgroundImage ||
null;

const _fullsizeURL = item.href || item.src || innerImage.src || null;
const _fullsizeURL =
item.dataset?.largeSrc ||
item.href ||
item.src ||
innerImage.src ||
null;

const _smallURL =
innerImage.currentSrc ||
Expand Down
9 changes: 0 additions & 9 deletions lib/cooked_post_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,6 @@ def convert_to_link!(img)
end
end

def each_responsive_ratio
SiteSetting
.responsive_post_image_sizes
.split("|")
.map(&:to_f)
.sort
.each { |r| yield r if r > 1 }
end

def optimize_image!(img, upload, cropped: false)
w, h = img["width"].to_i, img["height"].to_i
onebox = img.ancestors(".onebox, .onebox-body").first
Expand Down
9 changes: 9 additions & 0 deletions lib/cooked_processor_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,13 @@ def create_span_node(klass, content = nil)
span.content = content if content
span
end

def each_responsive_ratio
SiteSetting
.responsive_post_image_sizes
.split("|")
.map(&:to_f)
.sort
.each { |r| yield r if r > 1 }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { htmlSafe } from "@ember/template";
import { isAudio, isImage, isVideo } from "discourse/lib/uploads";
import eq from "truth-helpers/helpers/eq";

export default class extends Component {
export default class ChatUpload extends Component {
@service siteSettings;

@tracked loaded = false;
Expand Down Expand Up @@ -44,6 +44,10 @@ export default class extends Component {
return { width: width * ratio, height: height * ratio };
}

get imageUrl() {
return this.args.upload.thumbnail?.url ?? this.args.upload.url;
}

get imageStyle() {
if (this.args.upload.dominant_color && !this.loaded) {
return htmlSafe(`background-color: #${this.args.upload.dominant_color};`);
Expand All @@ -60,9 +64,10 @@ export default class extends Component {
<img
class="chat-img-upload"
data-orig-src={{@upload.short_url}}
data-large-src={{@upload.url}}
height={{this.size.height}}
width={{this.size.width}}
src={{@upload.url}}
src={{this.imageUrl}}
style={{this.imageStyle}}
loading="lazy"
tabindex="0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default {
},
callbacks: {
elementParse: (item) => {
item.src = item.el[0].src;
item.src = item.el[0].dataset.largeSrc || item.el[0].src;
},
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export default class ChatChannelSubscriptionManager {
const message = this.messagesManager.findMessage(data.chat_message.id);
if (message) {
message.cooked = data.chat_message.cooked;
message.uploads = cloneJSON(data.chat_message.uploads || []);
message.processed = true;
message.incrementVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export default class ChatChannelThreadSubscriptionManager {
const message = this.messagesManager.findMessage(data.chat_message.id);
if (message) {
message.cooked = data.chat_message.cooked;
message.uploads = cloneJSON(data.chat_message.uploads || []);
message.processed = true;
message.incrementVersion();
}
Expand Down
30 changes: 30 additions & 0 deletions plugins/chat/lib/chat/message_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,39 @@ def initialize(chat_message, opts = {})

def run!
post_process_oneboxes
process_thumbnails
DiscourseEvent.trigger(:chat_message_processed, @doc, @model)
end

def process_thumbnails
@model.uploads.each do |upload|
if upload.width <= SiteSetting.max_image_width &&
upload.height <= SiteSetting.max_image_height
return false
end

crop =
SiteSetting.min_ratio_to_crop > 0 &&
upload.width.to_f / upload.height.to_f < SiteSetting.min_ratio_to_crop

width = upload.thumbnail_width
height = upload.thumbnail_height

# create the main thumbnail
upload.create_thumbnail!(width, height, crop: crop)

# create additional responsive thumbnails
each_responsive_ratio do |ratio|
resized_w = (width * ratio).to_i
resized_h = (height * ratio).to_i

if upload.width && resized_w <= upload.width
upload.create_thumbnail!(resized_w, resized_h, crop: crop)
end
end
end
end

def large_images
[]
end
Expand Down
6 changes: 6 additions & 0 deletions plugins/chat/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ module ::Chat
object.chat_separate_sidebar_mode
end

add_to_serializer(
:upload,
:thumbnail,
include_condition: -> { SiteSetting.chat_enabled && SiteSetting.create_thumbnails },
) { object.thumbnail }

RETENTION_SETTINGS_TO_USER_OPTION_FIELDS = {
chat_channel_retention_days: :dismissed_channel_retention_reminder,
chat_dm_retention_days: :dismissed_dm_retention_reminder,
Expand Down
29 changes: 29 additions & 0 deletions plugins/chat/spec/system/uploads_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

context "when uploading to a new message" do
before do
Jobs.run_immediately!
channel_1.add(current_user)
sign_in(current_user)
end
Expand Down Expand Up @@ -39,6 +40,34 @@
expect(Chat::Message.last.uploads.count).to eq(1)
end

it "adds a thumbnail for large images" do
SiteSetting.create_thumbnails = true

chat.visit_channel(channel_1)
file_path = file_from_fixtures("huge.jpg", "images").path

attach_file(file_path) do
channel_page.open_action_menu
channel_page.click_action_button("chat-upload-btn")
end

expect { channel_page.send_message }.to change { Chat::Message.count }.by(1)

expect(channel_page).to have_no_css(".chat-composer-upload")

message = Chat::Message.last

try_until_success(timeout: 5) { expect(message.uploads.first.thumbnail).to be_present }

upload = message.uploads.first

# image has src attribute with thumbnail url
expect(channel_page).to have_css(".chat-uploads img[src$='#{upload.thumbnail.url}']")

# image has data-large-src with original image src
expect(channel_page).to have_css(".chat-uploads img[data-large-src$='#{upload.url}']")
end

it "adds dominant color attribute to images" do
chat.visit_channel(channel_1)
file_path = file_from_fixtures("logo.png", "images").path
Expand Down

0 comments on commit 8b46dc8

Please sign in to comment.