rendered_markdown: Move thumbnail rewriting to postprocess_content.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2024-07-23 12:19:01 -07:00 committed by Tim Abbott
parent edf34ada63
commit 94fe3f6194
4 changed files with 143 additions and 120 deletions

View File

@ -1,4 +1,7 @@
import {$t} from "./i18n";
import * as thumbnail from "./thumbnail";
import {user_settings} from "./user_settings";
import * as util from "./util";
let inertDocument: Document | undefined;
@ -84,5 +87,33 @@ export function postprocess_content(html: string): string {
);
}
}
for (const inline_img_thumbnail of template.content.querySelectorAll<HTMLImageElement>(
'div.message_inline_image > a > img[src^="/user_uploads/thumbnail/"]',
)) {
let thumbnail_name = thumbnail.preferred_format.name;
if (inline_img_thumbnail.dataset.animated === "true") {
if (
user_settings.web_animate_image_previews === "always" ||
// Treat on_hover as "always" on mobile web, where
// hovering is impossible and there's much less on
// the screen.
(user_settings.web_animate_image_previews === "on_hover" && util.is_mobile())
) {
thumbnail_name = thumbnail.animated_format.name;
} else {
// If we're showing a still thumbnail, show a play
// button so that users that it can be played.
inline_img_thumbnail
.closest(".message_inline_image")!
.classList.add("message_inline_animated_image_still");
}
}
inline_img_thumbnail.src = inline_img_thumbnail.src.replace(
/\/[^/]+$/,
"/" + thumbnail_name,
);
}
return template.innerHTML;
}

View File

@ -16,7 +16,6 @@ import * as realm_playground from "./realm_playground";
import * as rows from "./rows";
import * as rtl from "./rtl";
import * as sub_store from "./sub_store";
import * as thumbnail from "./thumbnail";
import * as timerender from "./timerender";
import * as user_groups from "./user_groups";
import {user_settings} from "./user_settings";
@ -338,32 +337,4 @@ export const update_elements = ($content: JQuery): void => {
.contents()
.unwrap();
}
$content
.find('div.message_inline_image > a > img[src^="/user_uploads/thumbnail/"]')
.each(function (): void {
const $inline_img_thumbnail = $(this);
let thumbnail_name = thumbnail.preferred_format.name;
if ($inline_img_thumbnail.attr("data-animated") === "true") {
if (
user_settings.web_animate_image_previews === "always" ||
// Treat on_hover as "always" on mobile web, where
// hovering is impossible and there's much less on
// the screen.
(user_settings.web_animate_image_previews === "on_hover" && util.is_mobile())
) {
thumbnail_name = thumbnail.animated_format.name;
} else {
// If we're showing a still thumbnail, show a play
// button so that users that it can be played.
$inline_img_thumbnail
.closest(".message_inline_image")
.addClass("message_inline_animated_image_still");
}
}
$inline_img_thumbnail.attr(
"src",
$inline_img_thumbnail.attr("src")!.replace(/\/[^/]+$/, "/" + thumbnail_name),
);
});
};

View File

@ -2,8 +2,11 @@
const {strict: assert} = require("assert");
const {zrequire} = require("./lib/namespace");
const {mock_esm, zrequire} = require("./lib/namespace");
const {run_test} = require("./lib/test");
const {user_settings} = require("./lib/zpage_params");
const thumbnail = mock_esm("../src/thumbnail");
const {postprocess_content} = zrequire("postprocess_content");
@ -31,3 +34,111 @@ run_test("postprocess_content", () => {
"</div>",
);
});
run_test("message_inline_animated_image_still", ({override}) => {
const thumbnail_formats = [
{
name: "840x560-anim.webp",
max_width: 840,
max_height: 560,
format: "webp",
animated: true,
},
{
name: "840x560.webp",
max_width: 840,
max_height: 560,
format: "webp",
animated: false,
},
{
name: "300x200-anim.webp",
max_width: 300,
max_height: 200,
format: "webp",
animated: true,
},
{
name: "300x200.webp",
max_width: 300,
max_height: 200,
format: "webp",
animated: false,
},
{
name: "300x200.jpg",
max_width: 300,
max_height: 200,
format: "jpg",
animated: false,
},
];
// TODO: Initialize the real thumbnail.ts rather than mocking it.
override(thumbnail, "preferred_format", thumbnail_formats[3]);
override(thumbnail, "animated_format", thumbnail_formats[2]);
assert.equal(
postprocess_content(
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" title="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/840x560.webp">' +
"</a>" +
"</div>",
),
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" target="_blank" rel="noopener noreferrer" aria-label="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/300x200.webp">' +
"</a>" +
"</div>",
);
// Now verify the behavior for animated images.
override(user_settings, "web_animate_image_previews", "always");
assert.equal(
postprocess_content(
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" title="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/840x560.webp" data-animated="true">' +
"</a>" +
"</div>",
),
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" target="_blank" rel="noopener noreferrer" aria-label="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/300x200-anim.webp" data-animated="true">' +
"</a>" +
"</div>",
);
// And verify the different behavior for other values of the animation setting.
override(user_settings, "web_animate_image_previews", "on_hover");
assert.equal(
postprocess_content(
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" title="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/840x560.webp" data-animated="true">' +
"</a>" +
"</div>",
),
'<div class="message_inline_image message_inline_animated_image_still">' +
'<a href="/user_uploads/path/to/image.png" target="_blank" rel="noopener noreferrer" aria-label="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/300x200.webp" data-animated="true">' +
"</a>" +
"</div>",
);
override(user_settings, "web_animate_image_previews", "never");
assert.equal(
postprocess_content(
'<div class="message_inline_image">' +
'<a href="/user_uploads/path/to/image.png" title="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/840x560.webp" data-animated="true">' +
"</a>" +
"</div>",
),
'<div class="message_inline_image message_inline_animated_image_still">' +
'<a href="/user_uploads/path/to/image.png" target="_blank" rel="noopener noreferrer" aria-label="image.png">' +
'<img data-original-dimensions="3264x2448" src="/user_uploads/thumbnail/path/to/image.png/300x200.webp" data-animated="true">' +
"</a>" +
"</div>",
);
});

View File

@ -23,7 +23,6 @@ mock_cjs("clipboard", Clipboard);
const realm_playground = mock_esm("../src/realm_playground");
const copied_tooltip = mock_esm("../src/copied_tooltip");
const thumbnail = mock_esm("../src/thumbnail");
user_settings.emojiset = "apple";
const rm = zrequire("rendered_markdown");
@ -139,14 +138,6 @@ const get_content_element = () => {
$content.set_find_results("div.spoiler-header", $array([]));
$content.set_find_results("div.codehilite", $array([]));
$content.set_find_results(".message_inline_video video", $array([]));
$content.set_find_results(
'div.message_inline_image > a > img[src^="/user_uploads/thumbnail/"]',
$array([]),
);
$content.set_find_results(
"div.message_inline_image img, div.message_inline_image video",
$array([]),
);
set_message_for_message_content($content, undefined);
@ -201,87 +192,6 @@ run_test("message_inline_video", () => {
window.GestureEvent = false;
});
run_test("message_inline_animated_image_still", ({override}) => {
const $content = get_content_element();
const $elem = $.create("img");
$elem.attr("data-animated", "false");
$elem.attr("src", "/path/to/image.png");
$content.set_find_results(
'div.message_inline_image > a > img[src^="/user_uploads/thumbnail/"]',
$array([$elem]),
);
const $stub = $.create("div.message_inline_image");
$elem.closest = (closest_opts) => {
assert.equal(closest_opts, ".message_inline_image");
return $stub;
};
const thumbnail_formats = [
{
name: "840x560-anim.webp",
max_width: 840,
max_height: 560,
format: "webp",
animated: true,
},
{
name: "840x560.webp",
max_width: 840,
max_height: 560,
format: "webp",
animated: false,
},
{
name: "300x200-anim.webp",
max_width: 300,
max_height: 200,
format: "webp",
animated: true,
},
{
name: "300x200.webp",
max_width: 300,
max_height: 200,
format: "webp",
animated: false,
},
{
name: "300x200.jpg",
max_width: 300,
max_height: 200,
format: "jpg",
animated: false,
},
];
// TODO: Initialize the real thumbnail.ts rather than mocking it.
override(thumbnail, "preferred_format", thumbnail_formats[1]);
override(thumbnail, "animated_format", thumbnail_formats[0]);
rm.update_elements($content);
assert.equal($elem.attr("src"), "/path/to/840x560.webp");
// Now verify the behavior for animated images.
$elem.attr("data-animated", "true");
override(user_settings, "web_animate_image_previews", "always");
rm.update_elements($content);
assert.equal($elem.attr("src"), "/path/to/840x560-anim.webp");
// And verify the different behavior for other values of the animation setting.
override(user_settings, "web_animate_image_previews", "on_hover");
rm.update_elements($content);
assert.equal($elem.attr("src"), "/path/to/840x560.webp");
assert.equal($stub.hasClass("message_inline_animated_image_still"), true);
override(user_settings, "web_animate_image_previews", "never");
rm.update_elements($content);
assert.equal($elem.attr("src"), "/path/to/840x560.webp");
assert.equal($stub.hasClass("message_inline_animated_image_still"), true);
});
run_test("user-mention", () => {
// Setup
const $content = get_content_element();