diff --git a/addons/html_builder/static/src/builder/options/image_gallery_option.js b/addons/html_builder/static/src/builder/options/image_gallery_option.js
index 370172757cb75..470e50907eaa7 100644
--- a/addons/html_builder/static/src/builder/options/image_gallery_option.js
+++ b/addons/html_builder/static/src/builder/options/image_gallery_option.js
@@ -1,5 +1,6 @@
import { registry } from "@web/core/registry";
import { Plugin } from "@html_editor/plugin";
+import { applyModifications, loadImageInfo } from "@html_editor/utils/image_processing";
class ImageGalleryOption extends Plugin {
static id = "ImageGalleryOption";
@@ -17,18 +18,21 @@ class ImageGalleryOption extends Plugin {
getActions() {
return {
addImage: {
- load: async () =>
- new Promise((resolve) => {
+ load: async ({ editingElement }) => {
+ let selectedImages;
+ await new Promise((resolve) => {
this.dependencies.media.openMediaDialog({
onlyImages: true,
multiImages: true,
save: (images) => {
- resolve(images);
+ selectedImages = images;
+ resolve();
},
});
- }),
- apply: ({ editingElement, loadResult: images }) => {
- addImages(images, editingElement);
+ });
+ await addImages(selectedImages, editingElement);
+ },
+ apply: () => {
this.dependencies.history.addStep();
},
},
@@ -43,7 +47,7 @@ class ImageGalleryOption extends Plugin {
/**
* Add the images selected in the media dialog in the gallery
*/
-function addImages(images, imageGalleryElement) {
+async function addImages(images, imageGalleryElement) {
const container = getContainer(imageGalleryElement);
if (!container) {
return;
@@ -52,6 +56,7 @@ function addImages(images, imageGalleryElement) {
const lastImage = getImages(imageGalleryElement).at(-1);
let lastIndex = lastImage ? getIndex(lastImage) : -1;
+ const imagePromises = [];
for (const image of images) {
image.classList.add(
"d-block",
@@ -62,9 +67,28 @@ function addImages(images, imageGalleryElement) {
"object-fit-cover"
);
image.dataset.index = ++lastIndex;
- // TODO: Change mimetype
+ imagePromises.push(transformImageToWebp(image));
+ }
+ await Promise.all(imagePromises);
+ if (images.length > 0) {
+ relayout(imageGalleryElement);
+ }
+}
+
+async function transformImageToWebp(image) {
+ await loadImageInfo(image);
+ if (
+ image.dataset.mimetype &&
+ !["image/gif", "image/svg+xml", "image/webp"].includes(image.dataset.mimetype)
+ ) {
+ // Convert to webp but keep original width.
+ image.dataset.mimetype = "image/webp";
+ const src = await applyModifications(image, {
+ mimetype: "image/webp",
+ });
+ image.src = src;
+ image.classList.add("o_modified_image_to_save");
}
- relayout(imageGalleryElement);
}
/**
diff --git a/addons/html_builder/static/src/builder/options/image_tool_option.js b/addons/html_builder/static/src/builder/options/image_tool_option.js
index 36c13cbabac6e..78d81e21565ff 100644
--- a/addons/html_builder/static/src/builder/options/image_tool_option.js
+++ b/addons/html_builder/static/src/builder/options/image_tool_option.js
@@ -5,7 +5,6 @@ import {
loadImage,
} from "@html_editor/utils/image_processing";
import { Component } from "@odoo/owl";
-import { loadBundle } from "@web/core/assets";
import { registry } from "@web/core/registry";
import { defaultBuilderComponents } from "../builder_components/default_builder_components";
import { AddElementOption } from "./add_element_option";
@@ -43,7 +42,6 @@ class ImageToolOptionPlugin extends Plugin {
// todo: This seems quite heavy for a simple reset. Retrieve some
// metadata, to load the image crop, to call processImageCrop, just to
// reset the crop. We might want to simplify this.
- await loadBundle("html_editor.assets_image_cropper");
const croppedImage = editingElement;
const container = document.createElement("div");
@@ -108,7 +106,6 @@ class ImageToolOptionPlugin extends Plugin {
}
},
load: async ({ editingElement, param: glFilterName }) => {
- await loadBundle("html_editor.assets_image_cropper");
editingElement.dataset.glFilter = glFilterName;
const newSrc = await applyModifications(editingElement, {
mimetype: getImageMimetype(editingElement),
diff --git a/addons/html_builder/static/tests/options/image_gallery.test.js b/addons/html_builder/static/tests/options/image_gallery.test.js
index 0cc092798b0fc..a990c2167b76f 100644
--- a/addons/html_builder/static/tests/options/image_gallery.test.js
+++ b/addons/html_builder/static/tests/options/image_gallery.test.js
@@ -9,7 +9,7 @@ const base64Img =
"data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA\n AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO\n 9TXL0Y4OHwAAAABJRU5ErkJggg==";
test("Add image in gallery", async () => {
- onRpc("/web/dataset/call_kw/ir.attachment/search_read", (test) => [
+ onRpc("/web/dataset/call_kw/ir.attachment/search_read", () => [
{
id: 1,
name: "logo",
@@ -19,6 +19,12 @@ test("Add image in gallery", async () => {
public: true,
},
]);
+
+ onRpc("/html_editor/get_image_info", () => {
+ expect.step("get_image_info");
+ return {};
+ });
+
await setupWebsiteBuilder(
`
@@ -43,7 +49,7 @@ test("Add image in gallery", async () => {
await click("img.o_we_attachment_highlight");
await animationFrame();
await click(".modal-footer button");
- await waitFor(":iframe img[data-index='6']");
+ await waitFor(":iframe .o_masonry_col img[data-index='6']");
const columns = queryAll(":iframe .o_masonry_col");
const columnImgs = columns.map((column) =>
@@ -51,4 +57,5 @@ test("Add image in gallery", async () => {
);
expect(columnImgs).toEqual([["1", "3", "4", "5", "6"], ["2"]]);
+ expect.verifySteps(["get_image_info"]);
});
diff --git a/addons/html_editor/static/src/main/media/image_crop_plugin.js b/addons/html_editor/static/src/main/media/image_crop_plugin.js
index ccdd103718b07..74093f9c30c83 100644
--- a/addons/html_editor/static/src/main/media/image_crop_plugin.js
+++ b/addons/html_editor/static/src/main/media/image_crop_plugin.js
@@ -2,7 +2,6 @@ import { registry } from "@web/core/registry";
import { Plugin } from "../../plugin";
import { _t } from "@web/core/l10n/translation";
import { ImageCrop } from "./image_crop";
-import { loadBundle } from "@web/core/assets";
import { withSequence } from "@html_editor/utils/resource";
export class ImageCropPlugin extends Plugin {
@@ -58,8 +57,6 @@ export class ImageCropPlugin extends Plugin {
this.dependencies.history.addStep();
};
- await loadBundle("html_editor.assets_image_cropper");
-
registry.category("main_components").add("ImageCropping", {
Component: ImageCrop,
props: { ...this.imageCropProps, onClose, onSave, document: this.document },
diff --git a/addons/html_editor/static/src/utils/image_processing.js b/addons/html_editor/static/src/utils/image_processing.js
index 40f04ab202397..8958e76c7ba60 100644
--- a/addons/html_editor/static/src/utils/image_processing.js
+++ b/addons/html_editor/static/src/utils/image_processing.js
@@ -1,6 +1,7 @@
import { rpc } from "@web/core/network/rpc";
import { pick } from "@web/core/utils/objects";
import { getAffineApproximation, getProjective } from "./perspective_utils";
+import { loadBundle } from "@web/core/assets";
// Fields returned by cropperjs 'getData' method, also need to be passed when
// initializing the cropper to reuse the previous crop.
@@ -495,6 +496,7 @@ function _getImageSizeFromCache(src) {
* @param {DOMStringMap} dataset dataset containing the cropperDataFields
*/
export async function activateCropper(image, aspectRatio, dataset, cropperOptions) {
+ await loadBundle("html_editor.assets_image_cropper");
const oldSrc = image.src;
const newSrc = await _loadImageObjectURL(image.getAttribute("src"));
image.src = newSrc;