lightbox: Replace image with media.

Since lightbox can also play videos now, replace image with media.
This commit is contained in:
Aman Agrawal 2023-09-14 05:41:33 +00:00 committed by Tim Abbott
parent 3a507b5752
commit b443916da1
5 changed files with 83 additions and 81 deletions

View File

@ -174,19 +174,19 @@ export function clear_for_testing() {
asset_map.clear();
}
export function render_lightbox_list_images(preview_source) {
export function render_lightbox_media_list(preview_source) {
if (!is_open) {
const images = Array.prototype.slice.call(
const media_list = Array.prototype.slice.call(
$(
".focused-message-list .message_inline_image img, .focused-message-list .message_inline_video video",
),
);
const $image_list = $("#lightbox_overlay .image-list").empty();
const $media_list = $("#lightbox_overlay .image-list").empty();
for (const img of images) {
const src = img.getAttribute("src");
for (const media of media_list) {
const src = media.getAttribute("src");
const className = preview_source === src ? "image selected" : "image";
const is_video = img.tagName === "VIDEO";
const is_video = media.tagName === "VIDEO";
let $node;
if (is_video) {
@ -207,22 +207,22 @@ export function render_lightbox_list_images(preview_source) {
.css({backgroundImage: "url(" + src + ")"});
}
$image_list.append($node);
$media_list.append($node);
// We parse the data for each image to show in the list,
// while we still have its original DOM element handy, so
// that navigating within the list only needs the `src`
// attribute used to construct the node object above.
parse_image_data(img);
parse_media_data(media);
}
}
}
function display_image(payload) {
render_lightbox_list_images(payload.preview);
render_lightbox_media_list(payload.preview);
$(".player-container, .video-player").hide();
$(".image-preview, .image-actions, .image-description, .download, .lightbox-zoom-reset").show();
$(".image-preview, .media-actions, .media-description, .download, .lightbox-zoom-reset").show();
const $img_container = $("#lightbox_overlay .image-preview > .zoom-element");
const img = new Image();
@ -230,53 +230,53 @@ function display_image(payload) {
$img_container.html(img).show();
const filename = payload.url?.split("/").pop();
$(".image-description .title")
$(".media-description .title")
.text(payload.title || "N/A")
.attr("aria-label", payload.title || "N/A")
.prop("data-filename", filename || "N/A");
$(".image-description .user").text(payload.user).prop("title", payload.user);
$(".media-description .user").text(payload.user).prop("title", payload.user);
$(".image-actions .open").attr("href", payload.source);
$(".media-actions .open").attr("href", payload.source);
const url = new URL(payload.source, window.location.href);
const same_origin = url.origin === window.location.origin;
if (same_origin && url.pathname.startsWith("/user_uploads/")) {
// Switch to the "download" handler, so S3 URLs set their Content-Disposition
url.pathname = "/user_uploads/download/" + url.pathname.slice("/user_uploads/".length);
$(".image-actions .download").attr("href", url.href);
$(".media-actions .download").attr("href", url.href);
} else if (same_origin) {
$(".image-actions .download").attr("href", payload.source);
$(".media-actions .download").attr("href", payload.source);
} else {
// If it's not same-origin, and we don't know how to tell the remote service to put a
// content-disposition on it, the download can't possibly download, just show -- so hide the
// element.
$(".image-actions .download").hide();
$(".media-actions .download").hide();
}
}
function display_video(payload) {
render_lightbox_list_images(payload.preview);
render_lightbox_media_list(payload.preview);
$(
"#lightbox_overlay .image-preview, .image-description, .download, .lightbox-zoom-reset, .video-player",
"#lightbox_overlay .image-preview, .media-description, .download, .lightbox-zoom-reset, .video-player",
).hide();
$(".player-container").show();
if (payload.type === "inline-video") {
$(".player-container").hide();
$(".video-player, .image-description").show();
$(".video-player, .media-description").show();
const $video = $("<video>");
$video.attr("src", payload.source);
$video.attr("controls", true);
$(".video-player").html($video);
$(".image-actions .open").attr("href", payload.source);
$(".media-actions .open").attr("href", payload.source);
const filename = payload.url?.split("/").pop();
$(".image-description .title")
$(".media-description .title")
.text(payload.title || "N/A")
.attr("aria-label", payload.title || "N/A")
.prop("data-filename", filename || "N/A");
$(".image-description .user").text(payload.user).prop("title", payload.user);
$(".media-description .user").text(payload.user).prop("title", payload.user);
return;
}
@ -309,10 +309,10 @@ function display_video(payload) {
$iframe.attr("allowfullscreen", true);
$("#lightbox_overlay .player-container").html($iframe);
$(".image-actions .open").attr("href", payload.url);
$(".media-actions .open").attr("href", payload.url);
}
export function build_open_image_function(on_close) {
export function build_open_media_function(on_close) {
if (on_close === undefined) {
on_close = function () {
$(".player-container iframe").remove();
@ -321,10 +321,10 @@ export function build_open_image_function(on_close) {
};
}
return function ($image) {
return function ($media) {
// if the asset_map already contains the metadata required to display the
// asset, just recall that metadata.
let $preview_src = $image.attr("src");
let $preview_src = $media.attr("src");
let payload = asset_map.get($preview_src);
if (payload === undefined) {
if ($preview_src.endsWith("&size=full")) {
@ -342,7 +342,7 @@ export function build_open_image_function(on_close) {
payload = asset_map.get($preview_src);
}
if (payload === undefined) {
payload = parse_image_data($image);
payload = parse_media_data($media);
}
}
@ -370,16 +370,18 @@ export function build_open_image_function(on_close) {
export function show_from_selected_message() {
const $message_selected = $(".selected_message");
let $message = $message_selected;
let $image = $message.find(".message_inline_image img, .message_inline_image video");
// This is a function to satisfy eslint unicorn/no-array-callback-reference
const media_classes = () => ".message_inline_image img, .message_inline_image video";
let $media = $message.find(media_classes());
let $prev_traverse = false;
// First, we walk upwards/backwards, starting with the current
// message, looking for an image to preview.
// message, looking for an media to preview.
//
// Care must be taken, since both recipient_row elements and
// message_row objects have siblings of different types, such as
// date elements.
while ($image.length === 0) {
while ($media.length === 0) {
if ($message.prev().length === 0) {
const $prev_message_group = $message.parent().prevAll(".recipient_row").first();
if ($prev_message_group.length === 0) {
@ -388,67 +390,65 @@ export function show_from_selected_message() {
break;
} else {
$message = $prev_message_group.find(".message_row").last();
$image = $message.find(".message_inline_image img, .message_inline_image video");
$media = $message.find(media_classes());
continue;
}
}
$message = $message.prev();
$image = $message.find(".message_inline_image img, .message_inline_image video");
$media = $message.find(media_classes());
}
if ($prev_traverse) {
while ($image.length === 0) {
while ($media.length === 0) {
if ($message.next().length === 0) {
const $next_message_group = $message.parent().nextAll(".recipient_row").first();
if ($next_message_group.length === 0) {
break;
} else {
$message = $next_message_group.find(".message_row").first();
$image = $message.find(
".message_inline_image img, .message_inline_image video",
);
$media = $message.find(media_classes());
continue;
}
}
$message = $message.next();
$image = $message.find(".message_inline_image img, .message_inline_image video");
$media = $message.find(media_classes());
}
}
if ($image.length !== 0) {
const open_image = build_open_image_function();
open_image($image);
if ($media.length !== 0) {
const open_media = build_open_media_function();
open_media($media);
}
}
// retrieve the metadata from the DOM and store into the asset_map.
export function parse_image_data(image) {
const $image = $(image);
const preview_src = $image.attr("src");
export function parse_media_data(media) {
const $media = $(media);
const preview_src = $media.attr("src");
if (asset_map.has(preview_src)) {
// check if image's data is already present in asset_map.
// check if media's data is already present in asset_map.
return asset_map.get(preview_src);
}
// if wrapped in the .youtube-video class, it will be length = 1, and therefore
// cast to true.
const is_youtube_video = Boolean($image.closest(".youtube-video").length);
const is_vimeo_video = Boolean($image.closest(".vimeo-video").length);
const is_embed_video = Boolean($image.closest(".embed-video").length);
const is_inline_video = Boolean($image.closest(".message_inline_video").length);
const is_youtube_video = Boolean($media.closest(".youtube-video").length);
const is_vimeo_video = Boolean($media.closest(".vimeo-video").length);
const is_embed_video = Boolean($media.closest(".embed-video").length);
const is_inline_video = Boolean($media.closest(".message_inline_video").length);
// check if image is descendent of #compose .preview_content
const is_compose_preview_image = $image.closest("#compose .preview_content").length === 1;
// check if media is descendent of #compose .preview_content
const is_compose_preview_media = $media.closest("#compose .preview_content").length === 1;
const $parent = $image.parent();
const $parent = $media.parent();
let type;
let source;
const url = $parent.attr("href");
if (is_inline_video) {
type = "inline-video";
// Render video from original source to reduce load on our own servers.
const original_video_url = $image.attr("data-video-original-url");
const original_video_url = $media.attr("data-video-original-url");
// `data-video-original-url` is only defined for external URLs in
// organizations which have camo enabled.
if (!original_video_url) {
@ -467,14 +467,14 @@ export function parse_image_data(image) {
source = $parent.attr("data-id");
} else {
type = "image";
if ($image.attr("data-src-fullsize")) {
source = $image.attr("data-src-fullsize");
if ($media.attr("data-src-fullsize")) {
source = $media.attr("data-src-fullsize");
} else {
source = preview_src;
}
}
let sender_full_name;
if (is_compose_preview_image) {
if (is_compose_preview_media) {
sender_full_name = people.my_full_name();
} else {
const $message = $parent.closest("[zid]");
@ -527,8 +527,8 @@ export function initialize() {
}
};
const open_image = build_open_image_function(reset_lightbox_state);
const open_video = build_open_image_function();
const open_image = build_open_media_function(reset_lightbox_state);
const open_video = build_open_media_function();
$("#main_div, #compose .preview_content").on(
"click",
@ -556,18 +556,20 @@ export function initialize() {
});
$("#lightbox_overlay").on("click", ".image-list .image", function () {
const $image_list = $(this).parent();
let $original_image;
const $media_list = $(this).parent();
let $original_media_element;
const is_video = $(this).hasClass("lightbox_video");
if (is_video) {
$original_image = $(
$original_media_element = $(
`.message_row video[src='${CSS.escape($(this).attr("data-src"))}']`,
);
} else {
$original_image = $(`.message_row img[src='${CSS.escape($(this).attr("data-src"))}']`);
$original_media_element = $(
`.message_row img[src='${CSS.escape($(this).attr("data-src"))}']`,
);
}
open_image($original_image);
open_image($original_media_element);
if (!$(".image-list .image.selected").hasClass("lightbox_video") || !is_video) {
pan_zoom_control.reset();
@ -585,7 +587,7 @@ export function initialize() {
if (coords.right > parentOffset) {
// add 2px margin
$image_list.animate(
$media_list.animate(
{
scrollLeft: coords.right - this.parentNode.clientWidth + 2,
},
@ -593,7 +595,7 @@ export function initialize() {
);
} else if (coords.left < this.parentNode.scrollLeft) {
// subtract 2px margin
$image_list.animate({scrollLeft: coords.left - 2}, 100);
$media_list.animate({scrollLeft: coords.left - 2}, 100);
}
});
@ -622,8 +624,8 @@ export function initialize() {
}
});
$("#lightbox_overlay").on("click", ".image-info-wrapper, .center", (e) => {
if ($(e.target).is(".image-info-wrapper, .center")) {
$("#lightbox_overlay").on("click", ".media-info-wrapper, .center", (e) => {
if ($(e.target).is(".media-info-wrapper, .center")) {
reset_lightbox_state();
overlays.close_overlay("lightbox");
}

View File

@ -225,7 +225,7 @@ export function initialize() {
});
delegate("body", {
target: ".image-info-wrapper > .image-description > .title",
target: ".media-info-wrapper > .media-description > .title",
appendTo: () => document.body,
onShow(instance) {
const title = $(instance.reference).attr("aria-label");

View File

@ -66,18 +66,18 @@
opacity: 1;
}
.image-info-wrapper {
.media-info-wrapper {
background-color: transparent;
display: flex;
flex-flow: row wrap;
}
.image-description,
.image-actions {
.media-description,
.media-actions {
margin: 20px;
}
.image-actions {
.media-actions {
flex-shrink: 0;
margin-left: auto;
@ -112,7 +112,7 @@
}
}
.image-description {
.media-description {
display: flex;
flex-direction: column;
max-width: calc(100% - 400px);
@ -226,13 +226,13 @@
@media only screen and ($ms_min <= width < $md_min) {
#lightbox_overlay {
.image-actions {
.media-actions {
width: 100%;
padding-left: 15px;
margin-top: 0;
}
.image-description {
.media-description {
max-width: calc(100% - 100px);
white-space: nowrap;
overflow: hidden;
@ -255,7 +255,7 @@
height: calc(100% - 101px - 104px);
}
.image-info-wrapper {
.media-info-wrapper {
align-items: flex-end;
}

View File

@ -1,10 +1,10 @@
<div id="lightbox_overlay" class="overlay new-style" data-overlay="lightbox" data-noclose="false">
<div class="image-info-wrapper">
<div class="image-description">
<div class="media-info-wrapper">
<div class="media-description">
<div class="title"></div>
<div class="user"></div>
</div>
<div class="image-actions">
<div class="media-actions">
<a class="button small lightbox-zoom-reset disabled">{{t "Reset zoom" }}</a>
<a class="button small open" rel="noopener noreferrer" target="_blank">{{t "Open" }}</a>
<a class="button small download" download>{{t "Download" }}</a>

View File

@ -57,7 +57,7 @@ test("pan_and_zoom", ({override}) => {
".focused-message-list .message_inline_image img, .focused-message-list .message_inline_video video",
{children: []},
);
const open_image = lightbox.build_open_image_function();
const open_image = lightbox.build_open_media_function();
open_image($img);
assert.equal(fetched_zid, 1234);
@ -94,7 +94,7 @@ test("youtube", ({override}) => {
{children: []},
);
const open_image = lightbox.build_open_image_function();
const open_image = lightbox.build_open_media_function();
open_image($img);
assert.equal($(".image-actions .open").attr("href"), href);
assert.equal($(".media-actions .open").attr("href"), href);
});